外部定義と仮定義
翻訳単位(プリプロセッサ後のソースファイル全体)のトップレベルでは、すべてのCプログラムは宣言のシーケンスであり、関数やオブジェクトを外部リンケージまたは内部リンケージで宣言します。これらの宣言は、どの関数外にも現れるため、外部宣言として知られています。
extern int n; // external declaration with external linkage int b = 1; // external definition with external linkage static const char *c = "abc"; // external definition with internal linkage int f(void) // external definition with external linkage { int a = 1; // non-external return b; } static void x(void) // external definition with internal linkage { }
外部宣言で宣言されたオブジェクトは静的な記憶域期間を持ち、そのためautoまたはregister指定子を使用できません(ただし、autoは型推論に使用できます(C23以降))。外部宣言によって導入された識別子はファイルスコープを持ちます。
目次 |
[編集] 仮定義
仮定義とは、初期化子がなく、かつ記憶域クラス指定子がないか、またはstatic指定子を持つ外部宣言のことです。
仮定義は、定義として機能する場合もそうでない場合もある宣言です。同じ翻訳単位内で、より前または後に実際の外部定義が見つかった場合、仮定義は単なる宣言として機能します。
int i1 = 1; // definition, external linkage int i1; // tentative definition, acts as declaration because i1 is defined extern int i1; // declaration, refers to the earlier definition extern int i2 = 3; // definition, external linkage int i2; // tentative definition, acts as declaration because i2 is defined extern int i2; // declaration, refers to the external linkage definition
同じ翻訳単位内に定義がない場合、仮定義はオブジェクトを空初期化する実際の定義として機能します。
int i3; // tentative definition, external linkage int i3; // tentative definition, external linkage extern int i3; // declaration, external linkage // in this translation unit, i3 is defined as if by "int i3 = 0;"
extern宣言とは異なり、以前の宣言が識別子のリンケージを確立した場合に変更しない、仮定義は同じ識別子の別の宣言とリンケージが異なる場合があります。同じ識別子に対して2つの宣言がスコープ内にあり、異なるリンケージを持つ場合、動作は未定義です。
static int i4 = 2; // definition, internal linkage int i4; // Undefined behavior: linkage disagreement with previous line extern int i4; // declaration, refers to the internal linkage definition static int i5; // tentative definition, internal linkage int i5; // Undefined behavior: linkage disagreement with previous line extern int i5; // refers to previous, whose linkage is internal
内部リンケージを持つ仮定義は、完全型である必要があります。
static int i[]; // Error, incomplete type in a static tentative definition int i[]; // OK, equivalent to int i[1] = {0}; unless redeclared later in this file
[編集] 一定義規則
各翻訳単位は、内部リンケージを持つすべての識別子に対して、ゼロまたは1つの外部定義(staticグローバル)を持つことができます。
内部リンケージを持つ識別子が、非VLA、(C99以降)sizeof、 _Alignof(C11以降)(C23まで)、 alignof(C23以降)、または typeof(C23以降)以外の任意の式で使用される場合、その翻訳単位にはその識別子に対して1つだけの外部定義が存在しなければなりません。
プログラム全体は、外部リンケージを持つすべての識別子に対して、ゼロまたは1つの外部定義を持つことができます。
外部リンケージを持つ識別子が、非VLA、(C99以降)sizeof、 _Alignof(C11以降)(C23まで)、 alignof(C23以降)、または typeof(C23以降)以外の任意の式で使用される場合、プログラム全体にはその識別子に対して1つだけの外部定義が存在しなければなりません。
[編集] 注釈
|
異なる翻訳単位のインライン定義は、一定義規則によって制約されません。インライン関数定義の詳細については、 |
(C99以降) |
ファイルスコープでの宣言におけるキーワードexternの意味については、記憶域期間とリンケージを参照してください。
宣言と定義の区別については、定義を参照してください。
仮定義は、内部リンケージを持つ識別子を前方宣言するさまざまなC89以前のアプローチを標準化するために導入されました。
[編集] 参考文献
- C23標準 (ISO/IEC 9899:2024)
- 6.9 外部定義 (p: TBD)
- C17標準 (ISO/IEC 9899:2018)
- 6.9 外部定義 (p: 113-116)
- C11標準 (ISO/IEC 9899:2011)
- 6.9 外部定義 (p: 155-159)
- C99標準 (ISO/IEC 9899:1999)
- 6.9 外部定義 (p: 140-144)
- C89/C90標準 (ISO/IEC 9899:1990)
- 3.7 外部定義