更奇怪的是,但仍然不是真正的编译器错误:如果将-32
值作为第 7 个或更高的可变参数传递,它可能会扩展为 64 位:
#include <stdio.h>
#include <stdarg.h>
/* long long int is a 64bit datatype on both Unix and Windows */
void setter(long long int arg, ...)
{
va_list args;
va_start(args, arg);
while(arg != 0) {
printf("0x%016llx %lld\n", arg, arg);
arg = va_arg(args, long long int);
}
va_end(args);
}
int main()
{
setter(-32, -32, -32, -32, -32, -32, -32, -32, 0);
return 0;
}
x64 Linux 上的输出是:
0xffffffffffffffe0 -32
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0xffffffffffffffe0 -32
0xffffffffffffffe0 -32
但是,x64 Windows 上的输出类似于:
0xffffffffffffffe0 -32
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00040800ffffffe0 1134700294832096
0x178bfbffffffffe0 1696726761565323232
0x00007ff6ffffffe0 140698833649632
0x00007ff6ffffffe0 140698833649632
这里的原因是,在 64 位x86 调用约定中,许多前导参数(System V AMD64 ABI 中为6 ,Microsoft x64 中为4)通过 CPU 寄存器传递,只有后续参数通过堆栈传递。
由于 64 位寄存器提供对其低 32 位的单独访问,编译器确实只设置了它们的低 32 位,因此没有值扩展到 64 位。
对于后续参数,它取决于编译器:
- gcc 确实将扩展的 64 位值推入堆栈
- MSVC cl.exe 确实将每个低 32 位写入堆栈,使每个高 32 位未初始化
- 不确定其他编译器
无论如何,堆栈上的每个参数都保留了 64 位。