为什么第一个和第三个printf工作方式如此不同?

时间:2013-07-07 17:20:33

标签: c printf

printf("line 5: %f\n",98);  //output is 0.000000
printf("line 6: %f\n",98.98); //output is 98.980000  
printf("line 5: %f\n",98);//though same as first printf statement but output is 98.979980

虽然第一个和最后一个printf语句完全相同,但它们的输出不同。为什么呢?

因为在期待浮动时会将int传递给printf,这就是为什么它工作很奇怪。但我的观点是为什么在最后一个打印声明中,它不是打印一些垃圾值或0,而是使用第二个printf语句的值,这就是打印的内容。

5 个答案:

答案 0 :(得分:5)

正如其他人已经说过的那样,将int传递给printf,当它期待double时会导致未定义的行为,并且可能发生任何事情。您可能对为什么程序在第三行打印98.979980而不是一些随机数的原因感兴趣。

将参数传递给堆栈上的printf。当第2行将98.98传递给printf时,它会被推入堆栈,首先是数字的最低部分

然后printf返回,并在第三行再次调用它,现在将98推入堆栈。在您的体系结构中,int类型似乎是32位; double类型的一半大小,因此这只会覆盖早先在堆栈上的98.98的下半部分。 98.98的上半部分仍在筹码中。

现在,对printf的第三次调用从堆栈中读取double。它读取的最重要的一半来自早先在堆栈上的98.98,而不太重要的一半来自98的二进制表示;这就是结果如此接近98.98的原因。由于98是如此小的数字,其最高有效位将为0,并且将98.98的最不重要的一半设置为大多数为零会给出较小的数字。

如果第3行使用的位数设置为1,则得到的结果大于98.98。例如,-1的二进制表示将其所有位设置为1,并得到:

printf("line 2: %f\n", 98.98); # 98.98
printf("line 3: %f\n", -1);    # 98.980042

如果编译器使用64位整数,或者首先传递具有最高有效部分的double,或者使用寄存器而不是堆栈来传递参数,则会得到非常不同的结果。

答案 1 :(得分:4)

因为您的程序会调用未定义的行为。 98的类型为int,但%f需要float(或double },由于默认促销规则)。

因此,由于printf()在转换说明符的类型和实际类型不匹配时具有UB,因此对其所做的任何事情都没有合理的解释。

答案 2 :(得分:2)

这是因为%f需要双参数。给int是未定义的行为。

  

ISO/IEC 9899:1999§7.19.6.1,9

     

如果任何参数不是相应转换规范的正确类型,则行为为Undefined。

Undefined behavior指的是行为无法预测的计算机代码。

至少使用gcc,如果启用了警告,您将收到相应的警告:

  

警告:格式'%f'需要输入'double',但参数2的类型为'int'

答案 3 :(得分:2)

%f预计double,但您传递的值为int。这是未定义的行为。

正确的是:

printf("line 5: %f\n",98.0); 
printf("line 6: %f\n",98.98); 
printf("line 5: %f\n",98.0);

答案 4 :(得分:1)

如果我们查看编译器生成的代码,我们会看到以下内容:

00401B5E|>MOV DWORD PTR SS:[ESP+0x4],0x62                          ; |||
00401B66|>MOV DWORD PTR SS:[ESP],arma_sto.00404024                 ; |||ASCII "line 5: %f\n"
00401B6D|>CALL <JMP.&msvcrt.printf>                                ; ||\printf
00401B72|>MOV DWORD PTR SS:[ESP+0x4],0x51EB851F                    ; ||
00401B7A|>MOV DWORD PTR SS:[ESP+0x8],0x4058BEB8                    ; ||
00401B82|>MOV DWORD PTR SS:[ESP],arma_sto.00404030                 ; ||ASCII "line 6: %f\n"
00401B89|>CALL <JMP.&msvcrt.printf>                                ; |\printf
00401B8E|>MOV DWORD PTR SS:[ESP+0x4],0x62                          ; |
00401B96|>MOV DWORD PTR SS:[ESP],arma_sto.00404024                 ; |ASCII "line 5: %f\n"
00401B9D|>CALL <JMP.&msvcrt.printf>                                ; \printf

因为您没有将两个98值转换为float,所以输出是随机的(基于堆栈)。 %f的有效输入是浮点指针编号,它在堆栈上占用两个条目。

第4行不起作用,因为您只提供了一个堆栈条目。

第5行工作正常,因为98.98是一个浮动指针号(需要两个堆栈条目)

第6行输出~98.98,因为00401B7A处的MOV未被撤消。这意味着第6行输出一个有效的浮点数,因为它有两个堆栈条目,但错误的浮动指针数,因为前一个数字还有一部分。

解决方案,强制转换为

printf("line 5: %f\n",(float)98);  //output is 98.000000
printf("line 6: %f\n",98.98); //output is 98.980000  
printf("line 5: %f\n",(float)98); //output is 98.000000