2

次のような C (linux(x86_64)) のコードがあります。

typedef struct 
{
   char k[32];
   int v;
}ABC;

ABC states[6] = {0};

ABC* get_abc()
{
   return &states[5];
}  

main() にいる間:

int main()
{
  ABC *p = get_abc();
     .
     .
     .
  printf("%d\n", p->v); 
} 

p->v にアクセスしているときに、printf ステートメントでセグメンテーション違反が発生します。gdb からデバッグしようとしましたが、「メモリにアクセスできません」と表示されます。ここで重要なことの 1 つは、このコードをコンパイルすると、gcc がABC *p = get_abc();に関する警告をスローすることです。ポインターを整数から変換しようとしています。ここでの私の質問は、get_abc() から構造体のアドレスを返しているのに、コンパイラがそのような警告を表示するのはなぜですか? なぜコンパイラはそれを整数と見なすのですか? 整数はx86_64でメモリアドレスを保持できないため、この警告によりセグメンテーション違反が発生していると思います。

どんな助けでも大歓迎です。

4

2 に答える 2

5

get_abc関数の前にプロトタイプを定義しmainます。その関数呼び出しが意味する前に関数プロトタイプが利用できない場合、コンパイラはデフォルトでその関数をint引数を渡して。を返すものとして扱いますint。ここでget_abcは実際には8バイトのアドレスが返されますが、その値は4バイトに抑制されており、ABC *p変数に格納されているため、クラッシュが発生します。

ABC* get_abc(); 
int main()
{
    ABC *p = get_abc();
}

注:このクラッシュは、intのサイズとアドレスのサイズが4バイトの32ビットマシンでは発生しません。これは、抑制が発生しないためです。しかし、その警告はそこにあります。

于 2013-01-29T18:11:47.420 に答える
2

すべてのコードを示したわけではありませんが、get_abc()main()関数は別々のソースファイルで定義されておりget_abc()、の呼び出しからは目に見える宣言がないことを確信できますmain()

次の宣言を含むヘッダーファイルを作成する必要がありますget_abc()

ABC *get_abc();

を定義するファイルとを#include定義するファイルの両方のヘッダー。(ヘッダーガードも必要です。)型の定義をそのヘッダーに移動する必要があります。get_abc()main()ABC

または、手っ取り早い回避策として、main()-の定義の前に明示的な宣言を追加できますが、宣言を正確に取得するかどうかはユーザー次第であるため、これはかなり脆弱な解決策です。

目に見える宣言がなく、宣言されていない関数はを返すと見なされintます。コンパイラは、への呼び出しを確認し、を返すかのようget_abc()に呼び出すコードを生成し、その値を暗黙的にポインタに変換します。陽気さが続きます。intint

注:nullポインター定数の特殊なケースを除いて、実際にはintからポインター型への暗黙の変換はありませんが、多くのコンパイラーは、歴史的な理由からそのような暗黙の変換を実装しています。また、「暗黙のint」ルールは1999バージョンの標準で削除されましたが、歴史的な理由から、多くのコンパイラがまだそれを実装しています。コンパイラには、より適切な警告を有効にするオプションが必要です。gccを使用している場合は、を試してくださいgcc -pedantic -std=c99 -Wall -Wextra

于 2013-01-29T18:17:18.913 に答える