0

次の簡単なコードを見てください

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」を配置する必要があるかをどのように判断しますか?
4

3 に答える 3

3

で使用scanf()するには、フォーマット文字列でshort指定する必要があります。%hd

あなたはに嘘をついているので、あなたはオーバーフローを引き起こしていますscanf()。警告をオンにします(-Wall少なくとも)。不一致についてGCCから苦情を受ける必要があります。(Cを学んでいる間、あなたが-Wall犯したばかげた間違いを捕まえるために使用してください。私のようにCで四半世紀以上プログラミングしているときは、まだそうでないことを確認するためにいくつかのフラグを追加します。ばかげた間違いを犯します。そして、コードができれいにコンパイルされることを常に確認します-Wall。)

Mac OSX10.7.5のGCC4.7.1は次のように述べています。

ss.c:6:4: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘short int *’ [-Wformat]
ss.c:7:4: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘short int *’ [-Wformat]
于 2012-11-03T08:17:20.947 に答える
1

Jonathan Leffler's answer は、 の問題を説明していscanfます。printfそれでは、どのようにうまく機能するのか不思議に思うかもしれません。

機能しているように見える理由printfは、それが可変引数関数、つまり可変数の引数を受け入れる関数だからです。C 標準 (したがって、Intel プラットフォームに実装された ABI) では、int より小さい整数型 (chars、shorts) のすべての値はスタック上の int として可変個引数関数に渡され、すべてのfloat値は として渡されdoubleます。ただし、このトリックは、実際の値ではなくscanfオブジェクトアドレスを受け取るでは機能しません。のコンテキストで「無害」と見なされるエラーでさえ、割り当てられるはずのオブジェクトをオーバーランさせますprintfscanf

于 2012-11-03T08:51:03.433 に答える