编辑:我已经知道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
答案 0 :(得分:16)
在睡觉前一晚给自己重复两次:
printf不是类型安全的。 printf不是类型安全的。 printf不是类型安全的。
该函数仅在您传递一个您承诺的类型的参数时才有效。其他一切都是未定义的行为。您保证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个字节,并且当发生时,最低有效四个字节被覆盖,因此浮点值仍然几乎相同。如果用原始浮点数中出现的相同字节覆盖四个字节,则得到完全相同的结果。
我可能会补充说,幸运的是,最重要的四个字节保持不变。在不同的情况下,它们可能已被其他东西覆盖。简而言之,“未定义的行为”。
答案 1 :(得分:2)
您使用的是错误的格式说明符。你传递一个浮点数(变成一个双精度数),所以printf
期望堆栈上有8个字节,但你传递的是int
,这是4个字节。写7.0
和9.0
将文字转换成双打。或者,正如Daniel在评论中所说,优化器可能会导致各种奇怪的行为发生。
答案 2 :(得分:2)
正如Barry Brown暗示的那样,你的7正在覆盖先前存储在堆栈中的8字节双字节的4个字节。根据堆栈增长的方式和双精度的字节顺序,7可能只是覆盖双尾数的最低有效位。因此,堆栈上的double实际上与上一次printf()调用中的double不完全相同;只是差异在%.5f格式中不可见。
Printf()不是严格类型安全的,但有些编译器会根据参数检查format语句,如果你这样做,会发出警告。
答案 3 :(得分:1)
你告诉printf
你传递的是double
(因为f
格式说明符),但实际上你传递了一个int。那是undefined behavior,几乎任何事情都可能发生。
答案 4 :(得分:1)
7是一个int,但是printf期望参数是float。上一个printf(9.334354)中的参数仍然位于堆栈上,单独的7不足以覆盖它。
如果您将7更改为7.0,则它可以正常工作。
答案 5 :(得分:0)
7
是一个整数。
7.0
是一个浮点数/双精度数。
printf
不是类型安全的,所以它期望一个double,但你传递一个整数。这意味着未定义的行为。