为什么第一个和第三个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语句的值,这就是打印的内容。
正如其他人已经说过的那样,当它期望一个double
时,将int
传递给printf
会导致未定义的行为,并且任何事情都可能发生。 您可能对程序在第三行打印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
s,或者使用寄存器而不是堆栈来传递参数,则会得到非常不同的结果。
因为您的程序调用未定义的行为。 98
是int
类型,但%f
期望float
(或由于默认促销规则而double
)。
因此,由于printf()
在转换说明符的类型和实际类型不匹配时具有UB,因此对它所做的任何事情都没有合理的解释。
%f
需要double
,但是您传递的是int
值。 这是未定义的行为。
适当的将是:
printf("line 5: %f\n",98.0); printf("line 6: %f\n",98.98); printf("line 5: %f\n",98.0);
这是因为%f
需要双参数。 给int
是未定义的行为。
ISO / IEC 9899:1999 , §7.19.6.1,9 :
如果任何参数不是相应转换规范的正确类型,则行为为Undefined。
未定义的行为是指行为不可预测的计算机代码。
至少使用gcc,如果启用了警告,您将收到相应的警告:
警告:格式’%f’需要输入’double’,但参数2的类型为’int’
如果我们查看编译器生成的代码,我们会看到以下内容:
00401B5E|>MOV DWORD PTR SS:[ESP+0x4],0x62 ;
00401B66|>MOV DWORD PTR SS:[ESP],arma_sto.00404024 ;
ASCII “line 5: %f\n” 00401B6D|>CALL
因为您没有将两个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