0

x86-64 コードの微妙なクラッシュを把握するのに半日を費やしただけなので、これは他の人への警告です。これが他の場所で扱われているのを見たことがありません。

適切な宣言なしで libc 関数を使用すると、gccは int を返すと想定します。たとえば、setlocale()はint setlocale()であると想定され、EAX で 32 ビットの int 値が返されます。

この戻り値を暗黙的または明示的なキャストによってポインターに変換しようとすると、呼び出された関数が RAX で有効な 64 ビン ポインター値を返したとしても、符号拡張によって 32 ビットから 64 ビットへの変換が強制されます。例えば

    char *p = setlocale(0, 0);      // bear with me for a second

にコンパイルされます

    1c: b8 00 00 00 00          mov    $0x0,%eax
    21: e8 00 00 00 00          callq  26 <hard_locale+0x26>
    26: 48 98                   cltq   ;   <--- eax is expanded in rax 

GCC は次のように伝えようとします。

    warning: initialization makes pointer from integer without a cast

明示的なキャストを追加すると、警告が次のように変わり、問題が示されます。

    warning: cast to pointer from integer of different size

運が良ければ何も起こりませんが、メモリ内のポインタに対して大きな値が返された場合は、次のようにめちゃくちゃになります。

    function returns in RAX: 0x07ffff7b9705e
    cltq considers EAX with negative sign: 0xf7b9705e
    now RAX is: 0xfffffffff7b9705e

あなたのポインターは無効です。

修正と解決策:

  • 常に適切な関数宣言を使用する

  • -Wall -Werrorは、x86-64 コンパイラではデフォルトでオンにする必要があります。

4

2 に答える 2

3

宣言されていない関数は無効ですC。追加するだけ-Werror=implicit-function-declarationで問題は解決します。他の警告(ほとんどは音節的な考慮事項)をエラーにする必要はありません。

これは、GCCが誤検知なしで無効なCを拒否するためのエラーとしての警告オプションのリストです。それはいくつかのことを見逃しています(GCCはキャッチをサポートしていません)が、ほとんど完全です:

  • -Werror=implicit-function-declaration
  • -Werror=implicit-int
  • -Werror=pointer-sign
  • -Werror-pointer-arith
  • -Werror=return-type
  • -std=c99(または-std=c11、必要に応じてなど)
  • -pedantic-errors(オプション。有効であるが完全に移植可能ではない一部のコードを拒否します)

-Werror=sequence-point最初にとをリストに追加し-Werror=array-boundsましたが、制約違反をマークせず、ランタイムUBのみをマークするため、誤検知があることに注意してください。そのため、このような警告を含むプログラムは、UBを呼び出すコードに到達できない限り、正しいプログラムである可能性があります(良い例として、if (sizeof(int)==sizeof(long)) { ... } else { ... }取得されていないブランチがUBを呼び出すブランチのようなブランチを考えてください。sizeofオペレーター)。

于 2013-03-11T15:17:33.357 に答える
1

古い冗談です。ここで同様の状況を説明しました: A nice 64-bit error in C .

これらのエラーやその他のエラーを回避するには、記事「実際のプログラムにおける 64 ビット エラーの例集」を読み、 Viva64 ルールを使用すると便利です。

于 2013-03-11T14:25:31.593 に答える