Namespaces
Variants

External and tentative definitions

From cppreference.net

翻訳単位の最上位レベルでは translation unit (すなわち、プリプロセッサ処理後のすべての #include を含むソースファイル)、すべてのCプログラムは declarations のシーケンスであり、これらは external or internal linkage を持つ関数とオブジェクトを宣言します。これらの宣言は、いずれの関数の外側にも現れるため external declarations として知られています。

extern int n; // 外部リンケージを持つ外部宣言
int b = 1;    // 外部リンケージを持つ外部定義
static const char *c = "abc"; // 内部リンケージを持つ外部定義
int f(void)    // 外部リンケージを持つ外部定義
{
    int a = 1; // 非外部
    return b;
}
static void x(void) // 内部リンケージを持つ外部定義
{
}

外部宣言で宣言されたオブジェクトは 静的記憶域期間 を持ち、そのため auto または register 指定子を使用できません (ただし auto は型推論に使用可能) (C23以降) 。外部宣言によって導入される識別子は ファイルスコープ を持ちます。

目次

暫定的な定義

仮定義( tentative definition )とは、初期化子を持たない外部宣言であり、 記憶クラス指定子 がないか、または static 指定子を持つものです。

暫定的定義 は、定義として機能する場合としない場合がある宣言です。同じ翻訳単位内で実際の外部定義が前方または後方に見つかった場合、暫定的定義は単なる宣言として機能します。

int i1 = 1;     // 定義、外部リンケージ
int i1;         // 暫定的定義、i1が定義済みのため宣言として機能
extern int i1;  // 宣言、前の定義を参照
extern int i2 = 3; // 定義、外部リンケージ
int i2;            // 暫定的定義、i2が定義済みのため宣言として機能
extern int i2;     // 宣言、外部リンケージ定義を参照

同じ翻訳単位に定義が存在しない場合、その暫定的定義はオブジェクトを empty-initializes する実際の定義として機能します。

int i3;        // 暫定的定義、外部リンケージ
int i3;        // 暫定的定義、外部リンケージ
extern int i3; // 宣言、外部リンケージ
// この翻訳単位では、i3は "int i3 = 0;" によって定義されたかのようになる

extern 宣言とは異なり、以前の宣言で識別子のリンケージが確立されている場合でもそれを変更しないのに対し、暫定的な定義(tentative definitions)は同じ識別子の他の宣言とリンケージが一致しない可能性があります。同じ識別子に対する2つの宣言がスコープ内に存在し、異なるリンケージを持つ場合、動作は未定義となります:

static int i4 = 2; // 定義、内部リンケージ
int i4;            // 未定義動作: 前の行とのリンケージ不一致
extern int i4;     // 宣言、内部リンケージの定義を参照
static int i5; // 暫定的定義、内部リンケージ
int i5;        // 未定義動作: 前の行とのリンケージ不一致
extern int i5; // 前の定義を参照(リンケージは内部)

内部リンケージを持つ暫定的な定義は完全型でなければなりません。

static int i[]; // エラー、静的暫定定義における不完全型
int i[]; // OK、後で再宣言されない限り int i[1] = {0}; と同等

One definition rule

各翻訳単位は、 内部リンケージ を持つすべての識別子の外部定義をゼロまたは一つ持つことができます ( static グローバル)。

内部リンケージを持つ識別子が、 非VLA以外の (C99以降) sizeof _Alignof (C11以降) (C23まで) alignof (C23以降) または typeof (C23以降) 以外の式で使用される場合、その識別子に対して翻訳単位内に外部定義が一つだけ存在しなければならない。

プログラム全体では、 外部リンケージ を持つすべての識別子の外部定義は0個または1個でなければなりません。

外部リンケージを持つ識別子が、 非VLA以外の (C99以降) sizeof _Alignof (C11以降) (C23まで) alignof (C23以降) または typeof (C23以降) 以外の式で使用される場合、その識別子に対してプログラム全体で唯一つの外部定義が存在しなければなりません。

注記

異なる翻訳単位でのインライン定義は、単一定義規則の制約を受けません。インライン関数定義の詳細については、 inline を参照してください。

(C99以降)

記憶域期間とリンケージ の説明を参照して、ファイルスコープでの宣言におけるキーワード extern の意味を確認してください

宣言と定義の区別については、 definitions を参照してください。

暫定的定義は、内部リンケージを持つ識別子を前方宣言するための様々なC89以前のアプローチを標準化するために考案されました。

参考文献

  • C23規格 (ISO/IEC 9899:2024):
  • 6.9 外部定義 (p: 未定)
  • 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 外部定義