编辑:我已经知道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();
}
Run Code Online (Sandbox Code Playgroud)
这是输出
9.33
9.33435
009
Run Code Online (Sandbox Code Playgroud)
Ker*_* SB 16
每晚睡前一次给自己重复两次:
printf不是类型安全的.printf不是类型安全的.printf不是类型安全的.
该函数仅在您传递了您承诺的类型的参数时才有效.其他一切都是未定义的行为.你保证double(通过%f)但提供int(文字的类型7),所以它是未定义的行为.你太无耻了.
(如果你感兴趣,我曾经详细解释实际输出.)
更新:由于您对此特定行为的解释感兴趣,因此这是我的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"
Run Code Online (Sandbox Code Playgroud)
现在的代码:
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
Run Code Online (Sandbox Code Playgroud)
你看到你所看到的东西的原因现在很简单.让我们暂时切换到完整的17位输出:
printf("%.17f\n", 9.334354);
printf("%.17f\n", 7);
Run Code Online (Sandbox Code Playgroud)
我们得到:
9.33435399999999937
9.33435058593751243
Run Code Online (Sandbox Code Playgroud)
现在让我们用"正确的"二进制组件替换整数:
printf("%.17f\n", 9.334354);
printf("%.17f\n", 1921946325);
Run Code Online (Sandbox Code Playgroud)
瞧:
9.33435399999999937
9.33435399999999937
Run Code Online (Sandbox Code Playgroud)
会发生什么是double占用堆栈上的8个字节的值0x4022AB30728E92D5.整数只占用4个字节,并且当发生时,最重要的四个字节被覆盖,因此浮点值仍然几乎相同.如果用原始浮点数中出现的相同字节覆盖四个字节,则得到完全相同的结果.
我可能会补充说,纯粹的运气是最重要的四个字节保持不变.在不同的情况下,它们可能已被其他东西覆盖.简而言之,"未定义的行为".