在处理从二进制文件中读取结构数据类型的简单项目时,我遇到了一个奇怪的printf格式类型mishmash。基本上我大多数时候使用%u
格式来显示无符号整数,而在我的struct中是一个类型为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
的工作方式和实施方式造成的吗?
答案 0 :(得分:10)
unsigned long long
将打印%llu
。使用不匹配的变量类型会调用未定义的行为。
引用C11
,章节§7.21.6.1
ll
(ell-ell)指定以下d,i,o,u,x或X转换说明符适用于a long long int或unsigned long long int argument;
关于UB,
[..]如果有任何论据 不是相应转换规范的正确类型,行为是 未定义。
答案 1 :(得分:2)
我无法使用我的编译器在我的计算机上复制此行为。但是,报告的行为在ideone:https://ideone.com/mCihqW处复制。
问题是您正在调用未定义的行为。调用printf
的第二个参数是无符号长long,但第一个格式指令是%u
。这是不匹配的。您应该使用%llu
作为格式指令。 printf
的格式指令必须与参数匹配。如果他们不这样做,该功能表现出不确定的行为。
没有人知道对未定义行为的回应是什么。
我怀疑你的计算机上发生了什么,你的编译器(也可能是在ideone.com上)是调用堆栈填充了
在看到格式指令中的第一个%u
时,printf
检查调用堆栈中format指令后面的四个字节(不是八个)。你可能在一台小型计算机上运行。解释为unsigned int的前四个字节包含1200,因此打印的是什么。
在看到下一个%u
时,printf
检查调用堆栈中的下四个字节。这四个字节是你的调用推送到调用堆栈的无符号long long的高位的一半。由于1200比2 32 小很多,因此无符号长long的上半部分都是零。所以printf
在你的钱包里打印0欧元。
在看到最后一个%u
时,printf
再次检查调用堆栈,这次从format指令结束后的8个字节开始。接下来的四个字节,作为unsigned int,包含2500.因此,printf
打印为传出。
如上所述,我在计算机上看不到这种行为。我看到了一种不同形式的未定义行为。不要试图理解未定义的行为。除非你真正知道自己在做什么并且愿意承担后果(例如,不可移植性,鼻子恶魔,硬盘擦除),否则不要调用未定义的行为。