さらに奇妙ですが、実際のコンパイラのバグではありません。-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;
}
また、x64Linuxでの出力は次のとおりです。
0xffffffffffffffe0 -32
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0x00000000ffffffe0 4294967264
0xffffffffffffffe0 -32
0xffffffffffffffe0 -32
ただし、x64Windowsでの出力は次のようになります。
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ビットが予約されています。