这个程序如何运作?

Laz*_*zer 88 c c++ memory printf endianness

#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", a);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它显示0!! 怎么可能?是什么原因?


我故意%dprintf声明中加入研究行为printf.

ken*_*ytm 238

那是因为%d期待一个,int但你提供了一个浮动.

使用%e/ %f/ %g打印浮动.


关于为什么打印0:浮点数double在发送之前转换为printf.小端的双重表示的数字1234.5是

00 00 00 00  00 4A 93 40
Run Code Online (Sandbox Code Playgroud)

A %d消耗32位整数,因此打印零.(作为测试,你printf("%d, %d\n", 1234.5f);可以得到输出0, 1083394560.)


至于为什么float转换为doubleprintf的原型int printf(const char*, ...),从6.5.2.2/7,

函数原型声明符中的省略号表示法导致参数类型转换在最后声明的参数之后停止.默认参数提升是在尾随参数上执行的.

从6.5.2.2/6开始,

如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有类型的参数float提升为double.这些被称为默认参数促销.

(感谢Alok发现这一点.)

  • 我相信你是12个人中唯一真正提供他想要的答案的人. (12认同)
  • 因为`printf`是一个可变函数,标准说对于可变函数,`float`在传递之前转换为`double`. (11认同)
  • 从C标准:"函数原型声明符中的省略号表示法导致参数类型转换在最后声明的参数之后停止.默认参数升级是在尾随参数上执行的." 和"...和类型为float的参数被提升为double.这些被称为*默认参数promotion*." (8认同)
  • +1最佳答案.它回答了"标准技术上正确"的原因和"您可能的实施"原因. (4认同)
  • 在`printf()`中使用不正确的格式说明符调用**未定义的行为**. (2认同)

Alo*_*hal 45

从技术上讲是没有 printf,每个库实现了自己,因此,你的努力学习方法printf做你正在做什么的行为是不会有太大的用处.您可能正在尝试研究printf系统上的行为,如果是这样,您应该阅读文档,并查看源代码printf是否适用于您的库.

例如,在我的Macbook上,我得到了1606416304你的程序的输出.

话虽如此,当你传递一个float变量函数时,float它作为一个传递double.所以,你的程序相当于声明a为a double.

要检查a的字节double,你可以在SO上看到这个最近问题的答案.

我们这样做:

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    unsigned char *p = (unsigned char *)&a;
    size_t i;

    printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
    for (i=0; i < sizeof a; ++i)
        printf("%02x ", p[i]);
    putchar('\n');
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当我运行上述程序时,我得到:

size of double: 8, int: 4
00 00 00 00 00 4a 93 40 
Run Code Online (Sandbox Code Playgroud)

因此,结果的前四个字节为double0,这可能就是为什么你得到0了你的printf呼叫输出.

为了获得更有趣的结果,我们可以稍微改变一下程序:

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    int b = 42;

    printf("%d %d\n", a, b);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当我在Macbook上运行上述程序时,我得到:

42 1606416384
Run Code Online (Sandbox Code Playgroud)

在Linux机器上使用相同的程序,我得到:

0 1083394560
Run Code Online (Sandbox Code Playgroud)


Mar*_*ner 20

%d说明符告诉printf期待一个整数.因此,浮点数的前四个(或两个,取决于平台)字节被解释为整数.如果它们恰好为零,则打印零

1234.5的二进制表示就像

1.00110100101 * 2^10 (exponent is decimal ...)
Run Code Online (Sandbox Code Playgroud)

使用C编译器float实际上表示为IEEE754双值,字节将是(如果我没有错误)

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000
Run Code Online (Sandbox Code Playgroud)

在具有少量字节序的英特尔(x86)系统(即最先出现的最低有效字节)上,此字节序列被反转,因此前四个字节为零.那就是printf打印出来的东西......

有关根据IEEE754的浮点表示,请参阅此Wikipedia文章.


Sha*_*ihi 7

这是因为二进制表示浮点数.转换为整数会使其为0.


Kil*_*oth 7

因为您调用了未定义的行为:您通过向其说明其参数类型违反了printf()方法的约定,因此编译器可以随意执行任何操作.它可以使程序输出"dksjalk是一个ninnyhead !!!" 从技术上讲,它仍然是正确的.


Gor*_*pik 5

原因是这printf()是一个非常愚蠢的功能.它根本不检查类型.如果你说第一个参数是一个int(这就是你所说的%d),它认为你只需要一个所需的字节int.在这种情况下,假设您的机器使用四字节int和八字节double(float转换为double内部printf()),前四个字节a将只是零,并打印出来.