printf - 奇怪的混杂结果。有人可以解释一下吗?
Posted
技术标签:
【中文标题】printf - 奇怪的混杂结果。有人可以解释一下吗?【英文标题】:printf - Weird type mishmash result. Can someone explain this? 【发布时间】:2015-09-04 19:46:20 【问题描述】:在处理一个从二进制文件读取结构数据类型的简单项目时,我遇到了一个奇怪的 printf 格式类型混杂。基本上,我大部分时间都使用%u
格式来显示无符号整数,而在我的结构中是一个类型为unsigned long long
的成员,使用格式字符显示此数据会导致一些奇怪,并且会浪费几个小时来搜索打错了。
这是一个例子:
struct bar
unsigned long long ll;
unsigned int i1;
unsigned int i2;
;
int main(void)
bar fubar;
fubar.ll = 1200;
fubar.i1 = 2500;
fubar.i2 = 450;
printf("Debt: %u Euro, Wallet: %u Euro, Outgoings: %u Euro.\n", fubar.ll, fubar.i1, fubar.i2);
return 0;
结果:
债务:1200 欧元,钱包:0 欧元,支出:2500 欧元。
使用 Visual Studio 2013 编译。
当然,当我使用%llu
格式时,一切都按预期工作。
这是因为printf
的工作方式和实现方式造成的吗?
【问题讨论】:
Cannot repro。你用的是什么编译器? 未定义的行为是未定义的。 相关:***.com/q/1748856/694576 【参考方案1】:unsigned long long
将与%llu
一起打印。使用不匹配类型的变量会调用未定义的行为。
引用C11
,第 7.21.6.1 章
ll
(ell-ell)指定后面的 d、i、o、u、x 或 X 转换说明符适用于 long long int 或 unsigned long long int 参数;
关于 UB,
[..] 如果有任何参数 不是相应转换规范的正确类型,行为是 未定义。
【讨论】:
【参考方案2】:我无法使用我的编译器在我的计算机上复制此行为。但是,报告的行为在 ideone 被复制:https://ideone.com/mCihqW。
问题在于您正在调用未定义的行为。对printf
的调用中的第二个参数是一个无符号长整型,但第一个格式指令是%u
。那是不匹配。您应该使用 %llu
作为格式指令。 printf
的格式指令必须与参数匹配。如果没有,该函数会表现出未定义的行为。
不知道对未定义行为的响应会是什么。
我怀疑您的计算机上发生的事情,以及您的编译器(可能也在 ideone.com 上)是调用堆栈填充了
指向格式指令的指针(四个或八个字节), 值为 1200(8 个字节)的无符号 long long, 一个值为 2500(四个字节)的无符号整数,并且 值为 450(另外四个字节)的无符号整数。在看到格式指令中的第一个%u
时,printf
会检查调用堆栈中格式指令之后的四个字节(不是八个)。您可能正在一台小端计算机上运行。被解释为无符号整数的前四个字节包含 1200,所以这就是打印的内容。
看到下一个%u
,printf
会检查调用堆栈中接下来的四个字节。这四个字节是您的调用推入调用堆栈的 unsigned long long 的高位一半。由于 1200 比 232 小很多,所以 unsigned long long 的上半部分全为零。所以printf
在你的钱包里打印出 0 欧元。
看到最后一个%u
,printf
再次检查调用堆栈,这次从格式指令结束后的八个字节开始。接下来的四个字节,作为一个无符号整数,包含 2500。这就是 printf
输出的输出。
如上所述,我没有在我的计算机上看到这种行为。我看到了另一种形式的未定义行为。不要试图理解未定义的行为。除非您真的知道自己在做什么并且愿意承受后果(例如,不可移植性、鼻恶魔、硬盘擦除),否则不要调用未定义的行为。
【讨论】:
以上是关于printf - 奇怪的混杂结果。有人可以解释一下吗?的主要内容,如果未能解决你的问题,请参考以下文章
有人可以解释为啥条件运算符和赋值运算符一起使用时表现奇怪吗?