为什么解释为双精度的整数呈现零?

Why does an integer interpreted as a double render zero?

printf("%f", 20); 导致输出 0.000000,而不是 20.000000。我猜这与 int 在内存中的表示方式以及 double 在内存中的表示方式有关。令我惊讶的是,无论我如何改变 20,例如通过增大数字,输出仍然是 0.000000。有人可以解释一下这背后的机制吗?

首先,这是undefined behavior%f 需要 float/double 类型的参数。传递 int 会产生不兼容的类型,因此它会调用 UB。

引用 C11,章节 §7.21.6.1,fprintf()

f,F

A double argument representing a floating-point number [...]

a float 也是允许的,因为默认参数提升规则,它将被提升为 double,这是那里的预期类型,因此 doublefloat 可以接受,但 int.

不行

...仅此而已。 UB,嗯,UB。您不能尝试用产生 UB 的代码来证明任何事情。


就是说,如果启用了适当的警告级别,代码应该根本无法编译。但是,如果您选择编译代码并生成 binary/assembly 代码,您会看到针对不同平台生成的不同代码。 中解释了其中一种情况,考虑到 Linux/OS X 上的 x86_64 arch。

问题是您的编译器假设 20int。您的选择是要么声明一个 float 变量并在此处输入它,要么添加一个类型转换。

例如

printf("%f", (double)20);

很可能您正在 platform/ABI 上编译代码,其中即使对于可变参数函数,数据也被传递到寄存器中,特别是 integer/floating 点值的不同寄存器。 x86_64 Linux/OS X 的行为就是这样。

调用方有一个整数要传递,因此将其放入rsi;另一方面,printf 需要一个浮点值,因此它尝试从 xmm0 读取它。无论您如何将整数参数更改为任何其他值 printf 都不会受到影响 - if 只会打印调用时留在 xmm0 中的任何内容。

您实际上可以通过将调用更改为:

来检查是否属于这种情况
printf("%f", 20, 123.45);

如果它像我描述的那样工作,你应该看到 123.45 被打印出来(这里的调用者将 123.45 放入 xmm0,因为它是传递给函数的第一个浮点参数;printf 表现为之前,但这次在 xmm0 中找到另一个值)。