Cが同じ変数の複数のグローバル宣言を許可し、複数のローカル宣言を許可しないのはなぜですか?


8

グローバル変数を複数回宣言すると、コンパイラは警告を出力しません。

ただし、たとえば関数内でローカル変数を複数回宣言すると、gccコンパイラーはエラーを出力し、ファイルをコンパイルしません。(私はgccに関して質問しますが、これはより一般的な言語設計の質問であり、gccについての質問ではありません。他のコンパイラーが同様に動作する可能性があるためです)。

この動作の説明は何ですか?


念のために言うと、グローバル変数は常に同じ型でしたか?
Walfrat 2017年

@Walfratはい、変数は常に同じ型で宣言されます。同じ名前でタイプが異なる2つの変数がグローバルに宣言されている場合、gccは「(変数)のタイプが競合しています」というエラーを出力する
yoyo_fun

3
ローカル変数は一度も宣言できません。あなたができることはそれを定義することだけです。変数を宣言することは、それが何であるかをコンパイラーに伝えます。変数を定義すると、コンパイラーにメモリーを割り当てるように指示されます。すべての変数を定義する必要があります。Cでは、グローバル変数の定義を宣言に複数回使用できます。ただし、プログラムextern int x;が宣言であるのみを持っている場合、メモリが変数に割り当てられる場所がないため、コンパイルは中止されます。
shawnhcorey 2017年

回答:


11

コーディングガイドラインによると:

プログラム全体を構成する翻訳単位とライブラリのセットでは、外部リンケージを使用した特定の識別子の各宣言は、同じオブジェクトまたは関数を示します。1つの変換単位内で、内部リンケージを持つ識別子の各宣言は、同じオブジェクトまたは関数を示します。リンケージのない識別子の各宣言は、一意のエンティティを示します。

ローカル変数にはリンケージがありません。名前があるので衝突が発生します。したがって、ローカル変数の複数宣言はできません。

グローバル変数には外部リンケージがあります。したがって、グローバル変数の複数の宣言が可能です。


7
この回答は問題ありませんが、フォローアップの明らかな質問は次のとおりです。この定義の背後にある理論的根拠は何ですか?
Doc Brown

多くのCの癖があるので、その根拠は「先行標準のCコンパイラがこのようにしただけ」です。「暫定的な定義」の複雑さを見ると、これが当てはまる可能性が高いです。
Sebastian Redl 2017年

9

@mscは、この動作の背後にあるルールを適切に紹介します。

グローバル変数を複数回宣言すると、コンパイラは警告を出力しません。

Cには、オブジェクト用の3種類のグローバル宣言がありますstatic

  1. 定義ではない宣言— extern int a;
  2. 定義でもある宣言- int a = 3;またはextern int a = 3;
  3. 暫定的な定義— int a;

タイプ1および3の複数の宣言が許可されていますが、最大1つの(タイプ2)定義が許可されています。


この動作の説明は何ですか?

これらのルールの動機についても質問している場合は、個別のコンパイルのサポートです。(翻訳単位を参照)。

プログラムを個別にコンパイルされた複数のファイルに分割するには、いくつかの機能が必要です。つまり、(a)必ずしも定義せず宣言できること、(b)前方に宣言することです。

1つの翻訳単位内で、別の翻訳単位のグローバル関数とデータを参照できる必要があります。また、ここでは、欠落している定義や誤った定義の重複を発見するためのエラーチェックも必要です。

同じ翻訳単位でグローバルを宣言し、後で定義する場合があります。これは、何らかの理由で前方宣言が必要な場合、または明示的な定義も提供する1つの変換単位内で(宣言を提供する)共通ヘッダーファイルを使用する場合に発生する可能性があります。

Cでの個別のコンパイルはグローバル関数とデータをリンクすることによって適用されるため、これらの機能はグローバルレベルで必要ですが、ローカルレベルでは必要ありません。

@mscが指摘しているように、ローカル変数はリンケージがないため、これらはローカル変数には必要ありません。

Cは(他の多くの言語と同様に)、ローカル変数のリンケージを提供しません。これは、言語が複数の個別の翻訳単位にわたる単一の関数をサポートしようとしないためです。

(もちろん、関数を複数のソースファイルにまたがることはできますが、複数の翻訳単位にはできません。)

仮の定義は宣言と同じように機能し、複数の翻訳単位で許可されます(他の宣言とうまく結合します)。ただし、プログラム全体に識別子の(一時的でない)定義がない場合、(1つの識別子の)複数の翻訳単位にわたる(1つ以上の)一時的な定義のセットは、初期化子が存在するオブジェクトの定義として使用されます。ゼロ。

これは、適切なサイズと配置でこれらを.BSSセクションに配置することで実装できます。リンカは、それらを実際の定義に一致する場合は一致させるか、またはそれらを互いに一致させて、BSSのスペースをゼロに設定します。


個別のコンパイルの概念は、暫定的な定義の機能がなくても完全にサポートできます—暫定的な定義は主に歴史的な理由で存在すると思います。(私がそれらが役に立たないと言っているわけではありません。もしその言語が今日作成されたとしても、これは不必要と見なされ、したがって提供されないかもしれません。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.