编辑:我已经知道 printf 不是类型安全的,我只是在寻找关于究竟发生了什么的解释(我的意思是描述未定义的行为)。
为什么如果我在第二个 printf 中打印“7”,程序会打印 9.334354。我知道如果我不写 7.0,就不会打印出来,但为什么要写第一个数字呢?
#include <stdio.h>
int main()
{
printf("%.2f\n", 9.334354);
printf("%.5f\n", 7);
printf("%03d\n", 9);
getchar();
}
这是输出
9.33
9.33435
009
最佳答案
每晚睡前对自己重复一次,持续两周:
printf 不是类型安全的。 printf 不是类型安全的。 printf 不是类型安全的。
该函数只有在您向它传递您 promise 的类型的参数时才会起作用。其他一切都是未定义的行为。您 promise double
(通过 %f
)但提供了一个 int
(文字 7
的类型),所以这是未定义的行为。真丢人。
(我做过一次 go into details 来解释实际输出,以防您感兴趣。)
更新:由于您对这个特定行为的解释感兴趣,这里是我的 x86/GCC4.6.2/-O3 上该代码的(相关)程序集:
首先是数据部分:
.LC0:
.long 1921946325
.long 1076013872 // 0x 4022AB30 728E92D5 is the binary rep of 9.334354
.LC1:
.string "%.2f\n"
.LC2:
.string "%.5f\n"
.LC3:
.string "%03d\n"
现在是代码:
fldl .LC0 // load number into fp register
fstpl 4(%esp) // put 64-bit double on the stack
movl $.LC1, (%esp) // first argument (format string)
call printf // call printf
movl $7, 4(%esp) // put integer VA (7) onto stack
movl $.LC2, (%esp) // first argument (format string)
call printf // call printf
movl $9, 4(%esp) // put integer VA (9) onto stack
movl $.LC3, (%esp) // first argument (format string)
call printf // call printf
现在您所见所闻的原因很简单。让我们暂时切换到完整的 17 位数字输出:
printf("%.17f\n", 9.334354);
printf("%.17f\n", 7);
我们得到:
9.33435399999999937
9.33435058593751243
现在让我们用“正确的”二进制组件替换整数:
printf("%.17f\n", 9.334354);
printf("%.17f\n", 1921946325);
瞧:
9.33435399999999937
9.33435399999999937
发生的是 double
在堆栈中占用 8 个字节,值为 0x4022AB30728E92D5
。整数只占用 4 个字节,碰巧,最低位 四个字节被覆盖,所以浮点值仍然几乎相同。如果用原始 float 中出现的相同字节覆盖这四个字节,则会得到完全相同的结果。
我可能会补充说,最重要的四个字节保持完好纯属幸运。在不同的情况下,它们可能已被其他内容覆盖。简而言之,“未定义的行为”。
关于c - 为什么 printf 取最后打印的数字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8017593/