为什么以下代码在 IA-32 和 x86-64 上运行完全不同?
#include <stdio.h>
int main() {
double a = 10;
printf("a = %d\n", a);
return 0;
}
在 IA-32 上,结果始终为 0。但是,在 x86-64 上,结果可以是 MAX_INT 和 MIN_INT 之间的任何值。这背后的原因是什么?
%d
实际上是用于打印int
。从历史上看,d
代表“十进制”,与 o
代表八进制和 x
代表十六进制形成对比。
对于打印
double
,您应该使用%e
、%f
或%g
。
使用错误的格式说明符会导致未定义的行为,这意味着任何事情都可能发生,包括意外的结果。
将与格式字符串中的格式说明符不匹配的参数传递给
printf()
是未定义的行为...并且在未定义的行为下,任何事情都可能发生,并且结果不一定从一个实例到另一个实例一致 - 所以应该避免。
至于为什么您会看到 x86(32 位)和 x86-64 之间的差异,很可能是因为每种情况下传递参数的方式不同。
在 x86 情况下,
printf()
的参数可能在堆栈上传递,在 4 字节边界上对齐——因此,当 printf()
处理 %d
说明符时,它会从堆栈,实际上是 int
的低 4 个字节。 由于 a
为 10,这些字节没有设置位,因此它们被解释为 a
值 0。在 x86-64 情况下,
int
的参数都在寄存器中传递(尽管如果有足够的参数,有些参数会在堆栈上)...但是
printf()
参数与 double
相比在不同的寄存器中传递
参数(例如 %xmm0 而不是 %rsi)。 因此,当 int
尝试处理 printf()
参数以匹配 int
说明符时,它会从传入 %d
的不同寄存器中获取它,并使用寄存器中留下的任何垃圾值而不是a
的低字节,并将其解释为一些垃圾 a
值。