web-dev-qa-db-ja.com

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

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

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

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

8
yoyo_fun

coding-guidelines によると:

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

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

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

11
msc

@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)forward宣言

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

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

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

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

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

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

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

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


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

9
Erik Eidt