2015-09-04 63 views
2

在处理从二进制文件读取结构数据类型的简单项目时,我遇到了一个奇怪的printf格式类型混杂。基本上我大多数时候使用%u格式来显示无符号整数,而在我的结构中是一个类型为unsigned long long的成员显示这个格式字符的数据会导致一些奇怪的情况和几个迷失的小时来搜索错误类型。printf - 怪异的混杂结果。有人可以解释这个吗?

下面是一个例子:

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

[Can not repro](http://coliru.stacked-crooked.com/a/2bfb44c2b06fb84c)。你使用什么编译器? – Borgleader

+6

未定义的行为未定义。 –

+1

相关:http://stackoverflow.com/q/1748856/694576 – alk

回答

2

我无法用我的编译器在我的电脑上复制此行为。但是,报告的行为在ideone处被复制:https://ideone.com/mCihqW

问题是您正在调用未定义的行为。 printf调用中的第二个参数是一个无符号long long,但第一个格式指令是%u。这是不匹配的。您应该使用%llu作为格式指令。格式指令printf必须与参数匹配。如果他们不这样做,该函数会显示未定义的行为。

不知道对未定义行为的反应是什么。

我怀疑你的电脑上发生了什么事,你的编译器(大概是在ideone上)。COM以及)是调用堆栈中填充

  • 的指针格式指令(四个或八个字节),
  • 一个无符号长长具有值1200(八个字节),
  • 的值为2500的无符号整数(四个字节)和
  • 一个值为450(另四个字节)的无符号整数。

在格式指令看到第一%uprintf检查调用堆栈格式指令后的四个字节(不是八颗)。你大概是在一台小型的电脑上运行。那些被解释为无符号整数的前四个字节包含1200,所以这就是打印的内容。

看到下一个%u,printf检查调用堆栈中的下四个字节。这四个字节是无符号长整数的高位一半,您的呼叫被推入调用堆栈。由于1200远小于2 ,所以无符号long long的上半部分都是位0。所以printf在您的钱包中打印0欧元。

看到最后的%u,printf再次检查调用堆栈,此时从格式指令结束后的8个字节开始。接下来的四个字节,作为一个无符号整数,包含2500.所以这就是printf输出。

如上所述,我在计算机上看不到这种行为。我看到了一种不确定的行为。不要试图理解未定义的行为。除非你真的知道自己在做什么,并且愿意忍受后果(例如,不可移植性,鼻恶魔,硬盘擦除),否则不要调用未定义的行为。

10

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,

[..]如果任何参数是 类型不正确的相应的转换说明书中,行为是 未定义。

相关问题