为什么printf会打印最后一个数字?

时间:2011-11-05 01:49:08

标签: c printf

编辑:我已经知道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

6 个答案:

答案 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.09.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,但你传递一个整数。这意味着未定义的行为。