次の簡単なコードを見てください
int main()
{
short x = 0, y = 0;
scanf("%d", &x);
scanf("%d", &y);
printf("%d %d\n", x, y);
return 0;
}
このプログラムに4と5を入力すると、出力に4と5が含まれると予想されます。Windows(mingw)のGCC 4.6.2では、出力として0と5が生成されます。だから私は少し掘り下げました。これは生成されたアセンブリコードです
movw $0, 30(%esp)
movw $0, 28(%esp)
leal 30(%esp), %eax
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _scanf
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _scanf
アセンブラのコーディングはあまり行っていませんが、上記のコードは正しくありません。xがespの30バイトのオフセットに配置され、yがespの28バイトのオフセットに配置されてから、それらのアドレスがscanfに渡されることを示唆しているようです。したがって、xとyのアドレスがlong int(4バイトアドレス)として処理されると、次のようになります。最初の呼び出しでバイト[30,34)が値0x00000004に設定され、2番目の呼び出しでバイトが設定されます。 [28、32)を値0x00000005に。ただし、これはリトルエンディアンのマシンであるため、30から[0x04 0x00 0x00 0x00]、28から[0x05 0x00 0x00 0x00]になります。これにより、バイト番号30が0にリセットされます。
scanfsの順序を逆にしてみたところ、うまくいきました(出力は4と5として出力されました)。そのため、最初に小さい方のオフセットが埋められ、次に後者の(大きい方の)オフセットが埋められました。
GCCがこれを台無しにした可能性があるのは馬鹿げているように見えた。そこで、MSVCを試してみましたが、生成されたアセンブリには1つの大きな違いがありました。変数はオフセット-4と-8に配置されました(つまり、コメントには2バイトと記載されていましたが、長さは4バイトと見なされていました)。コードの一部は次のとおりです。
_TEXT SEGMENT
_x$ = -8 ; size = 2
_y$ = -4 ; size = 2
_main PROC
push ebp
mov ebp, esp
sub esp, 8
xor eax, eax
mov WORD PTR _x$[ebp], ax
xor ecx, ecx
mov WORD PTR _y$[ebp], cx
lea edx, DWORD PTR _x$[ebp]
push edx
push OFFSET $SG2470
call _scanf
add esp, 8
lea eax, DWORD PTR _y$[ebp]
push eax
push OFFSET $SG2471
call _scanf
add esp, 8
私の質問は2つの部分に分かれています。
- 個人用のLinuxボックスを自由に使用することはできません。これはGCCの問題ですか、それともmingwの問題だけですか?
しかし、もっと重要なのは、
- これはまったくバグですか?コンパイラは、2バイトオフセットまたは4バイトオフセットのどちらに「short」を配置する必要があるかをどのように判断しますか?