2

次のスニペットは正常に動作します

extern int i;
int i;

int main(){
    return 0;
}

ここで私が得たのは、「i」が宣言されてから定義されていることです。定義は 1 つしかないので、まったく問題ありません。

int main(){
    extern int i;
    int i;
    return 0;
}

さて、上記のものは次のエラーを出します

new.cpp: In function ‘int main()’:
new.cpp:5:6: error: redeclaration of ‘int i’
  int i;
      ^
new.cpp:4:13: note: previous declaration ‘int i’
  extern int i;

ここで何が問題なのですか?ここでも 'i' の定義は 1 つです。

4

2 に答える 2

3

違いを理解するには、C の仮定義と呼ばれる概念に慣れる必要があります。C 標準を引用すると、次のようになります。

C11、ドラフト、§6.9.2、外部オブジェクト定義

ファイル スコープを持ち、初期化子がなく、ストレージ クラス指定子がないか、またはストレージ クラス指定子が static であるオブジェクトの識別子の宣言は、暫定的な定義を構成します。翻訳単位に識別子の暫定的な定義が 1 つ以上含まれており、翻訳単位にその識別子の外部定義が含まれていない場合、動作は、翻訳単位にその識別子のファイル スコープ宣言が含まれているかのように、複合型は次のようになります。 0 に等しい初期化子を使用して、翻訳単位の末尾を指定します。

最初のスニペットにあるのは、 の暫定的な定義にすぎませんi。1 つのオブジェクトに対して必要な数の仮の定義を設定できます (ただし、定義は 1 つしか許可されません)。

int i; // tentative definition
int i; // tentative definition
int i; // tentative definition

int main(void) {
   return 0;
}

有効です。

ここでiは、外部リンクがあり、仮定義されています。が同じ翻訳単位のどこかで定義されている場合i、それが の実際の定義になりiます。翻訳単位に isの他の定義iが見つからない場合、これは次のように定義されているかのように完全な定義になります。

int i = 0;

int main(void) {
   return 0;
}

しかし、2 番目のスニペットint i;は暫定的な定義ではありません。仮定義できるのは、外部結合を持つオブジェクトだけです。2 番目のスニペットでは、宣言extern int i;i、外部リンケージを使用して別の場所で定義されていると述べています。しかし、次の行int i;i、リンケージなしで定義されていることを示しています (ローカル自動変数にはリンケージがありません。これは暫定的な定義ではありません) したがって、 の定義に矛盾がありますi。したがって、最初の 1 つのスニペットは問題ありませんが、2 番目のスニペットはそうではありません。

于 2015-09-12T06:57:02.937 に答える
0

i2 番目のケースでは、1 つのスコープにの 2 つの宣言があります。i1 つは、「この関数の外部で定義された変数がある」というものです。iもう1つは、「この関数内に定義された変数があります」と言います。新しいスコープがないと、それは許可されません。

ルールは関数内と関数外で異なります。

以下を使用できることに注意してください。

#include <stdio.h>

int i = 21;

int main(void)
{
    extern int i;
    i = 37;
    {
    int i = 57;
    printf("%d\n", i);
    }
    printf("%d\n", i);
    return 0;
}

これは問題なくコンパイルされ ( -WshadowGCC または Clang を使用するときにコンパイル オプションに含めない限り)、出力に と が生成57されます (コメントのCoffeeAndCode37指摘されているように)。

ソースファイル間で変数を共有するにはどうすればよいですか?も参照してください。extern

于 2015-09-12T06:28:28.430 に答える