为什么第一个和第三个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,或者使用寄存器而不是堆栈来传递参数,则会得到非常不同的结果。

因为您的程序调用未定义的行为。 98int类型,但%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 ; ||\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 ; |\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 ; \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