c - 为什么 printf 取最后打印的数字?

标签 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

最佳答案

每晚睡前对自己重复一次,持续两周:

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/

相关文章:

缓存内存优化数组转置 : C

c - 如何在 Delphi 中比较两个数字是否相等?

c - 在C中初始化一个指向常量对象的常量指针

c - 打印 BASE62 中的前 N ​​个数字

c - 为什么我的 printf 这样做?

c# - C 和 C# 中的字符串解析器

c++ - 在 CMake 中,如何测试编译器是否为 Clang?

c - "sprintf"使用 Visual Studio 2013 引发异常

c - 显示带前导零的十六进制整数

terminal - 打印到终端会导致 OOM(内存不足)吗?