なぜ以下がコンパイルされないのですか?
...
extern int i;
static int i;
...
ただし、順序を逆にすると、正常にコンパイルされます。
...
static int i;
extern int i;
...
ここで何が起こっているのですか?
なぜ以下がコンパイルされないのですか?
...
extern int i;
static int i;
...
ただし、順序を逆にすると、正常にコンパイルされます。
...
static int i;
extern int i;
...
ここで何が起こっているのですか?
これは、外部または内部のリンクを宣言する複雑さについて説明しているときに、C++標準の例として具体的に示されています。これはセクション7.1.1.7にあり、次のように機能します。
static int b ; // b has internal linkage
extern int b ; // b still has internal linkage
extern int d ; // d has external linkage
static int d ; // error: inconsistent linkage
extern
セクション3.5.6では、この場合の動作について説明します。
何が起こっているのかはこれです:(static int i
この場合)は定義であり、は内部リンクがあるstatic
ことを示します。コンパイラがシンボルがすでに存在することを確認し、シンボルがすでに内部リンケージを持っていることを受け入れて続行した後に発生するi
場合。これが、2番目の例がコンパイルされる理由です。extern
static
一方extern
、宣言は、シンボルに外部リンケージがあることを暗黙的に示しますが、実際には何も作成しません。i
最初の例には外部リンケージがあるものとして登録されていないため、コンパイラはi
外部リンケージがあると登録しますが、コンパイラstatic
は内部リンケージがあるという互換性のないステートメントを見つけてエラーを出します。
言い換えれば、それは宣言が定義よりも「ソフト」だからです。たとえば、同じことをエラーなしで複数回宣言できますが、定義できるのは1回だけです。
これがCでも同じかどうかはわかりません(ただし、以下のnetcoderの回答は、C標準に同じ要件が含まれていることを示しています)。
Cの場合、標準を引用すると、C11 6.2.2:識別子のリンク:
3)オブジェクトまたは関数のファイルスコープ識別子の宣言にストレージクラス指定子が含まれている
static
場合、識別子には内部リンクがあります。4)その識別子の前の宣言が表示されるスコープでストレージクラス指定子
extern
を使用して宣言された識別子の場合、前の宣言が内部または外部のリンケージを指定している場合、後の宣言での識別子のリンケージは事前の宣言で指定されたリンケージ。以前の宣言が表示されていない場合、または以前の宣言でリンクが指定されていない場合、識別子には外部リンクがあります。
(強調-私の)
これが2番目の例を説明しています(i
内部リンケージがあります)。最初のものに関しては、私はそれが未定義の振る舞いであるとかなり確信しています:
7)変換ユニット内で、内部リンケージと外部リンケージの両方で同じ識別子が表示される場合、動作は定義されていません。
...識別子が内部リンケージで宣言される前extern
に表示されるため、6.2.2/4は適用されません。このように、内部と外部の両方のリンケージがあるので、それはUBです。i
コンパイラが診断を発行した場合、幸運なことに私は推測します。エラーなしでコンパイルでき、標準に準拠している可能性があります。
C ++:
7)storage-class-specifierを使用せずに名前空間スコープで宣言された名前は、以前の宣言のために内部リンケージがあり、 constとして宣言されていない場合を除き、外部リンケージがあります。constとして宣言され、externとして明示的に宣言されていないオブジェクトには、内部リンクがあります。
したがって、最初のものは最初にi
外部リンケージを与え、その後内部を与えようとします。
2行目は最初に内部リンケージを提供し、2行目は以前に内部として宣言されていたため、外部リンケージを提供しようとはしません。
8)所与のエンティティの連続した宣言によって暗示されるリンクは、同意するものとします。つまり、特定のスコープ内で、同じ変数名または関数名の同じオーバーロードを宣言する各宣言は、同じリンケージを意味するものとします。ただし、オーバーロードされた関数の特定のセット内の各関数は、異なるリンケージを持つことができます。
[ 例:
[...]
static int b; // b has internal linkage
extern int b; // b still has internal linkage
[...]
extern int d; // d has external linkage
static int d; // error: inconsistent linkage
[...]
Microsoft Visual Studioでは、どちらのバージョンも問題なくコンパイルされます。Gnu C ++では、エラーが発生します。
どのコンパイラが「正しい」かわかりません。いずれにせよ、両方の行があることはあまり意味がありません。
extern int i
整数i
が他のモジュール(オブジェクトファイルまたはライブラリ)で定義されていることを意味します。これは宣言です。コンパイラはこのオブジェクトにストレージを割り当てませんi
が、プログラム内の別の場所で使用している場合は変数を認識します。
int i
にストレージを割り当てるようにコンパイラに指示しi
ます。これは定義です。他のC++(またはC)ファイルにが含まれているint i
場合、リンカはintiが2回定義されていると文句を言います。
static int i
上記と同様ですが、i
ローカルの追加機能があります。宣言していても、他のモジュールからはアクセスできませんextern int i
。人々は私をローカライズし続けるために(この文脈では)キーワードstaticを使用しています。
したがって、i
両方が別の場所で定義されていると宣言され、モジュール内で静的として定義されていると、エラーのように見えます。Visual Studioはそれについて沈黙しており、g ++は特定の順序でのみ沈黙していますが、どちらの方法でも、同じソースコードに両方の行を含めるべきではありません。