スコープ
Cプログラムに現れる各識別子は、ソースコードの(断続的かもしれない)ある部分、すなわちそのスコープでのみ可視(つまり、使用可能)です。
スコープ内では、識別子は、それらが異なる名前空間にある場合のみ、複数のエンティティを指定できます。
Cには4種類のスコープがあります。
- ブロック スコープ
- ファイル スコープ
- 関数 スコープ
- 関数プロトタイプ スコープ
目次 |
[編集] ネストしたスコープ
同じ識別子で命名された2つの異なるエンティティが同時にスコープ内にあり、それらが同じ名前空間に属する場合、スコープはネストしています(他のいかなるスコープの重なりも許可されません)。そして、内側のスコープに現れる宣言は、外側のスコープに現れる宣言を隠します。
// The name space here is ordinary identifiers. int a; // file scope of name a begins here void f(void) { int a = 1; // the block scope of the name a begins here; hides file-scope a { int a = 2; // the scope of the inner a begins here, outer a is hidden printf("%d\n", a); // inner a is in scope, prints 2 } // the block scope of the inner a ends here printf("%d\n", a); // the outer a is in scope, prints 1 } // the scope of the outer a ends here void g(int a); // name a has function prototype scope; hides file-scope a
[編集] ブロック スコープ
複合文(関数本体を含む)内、またはif、switch、for、while、またはdo-while文のいずれかの式、宣言、または文に現れるもの内の識別子のスコープは、宣言点から始まり、宣言されたブロックまたは文の終端で終了します(C99以降)(C99以降)。
void f(int n) // scope of the function parameter 'n' begins { // the body of the function begins ++n; // 'n' is in scope and refers to the function parameter // int n = 2; // error: cannot redeclare identifier in the same scope for(int n = 0; n<10; ++n) { // scope of loop-local 'n' begins printf("%d\n", n); // prints 0 1 2 3 4 5 6 7 8 9 } // scope of the loop-local 'n' ends // the function parameter 'n' is back in scope printf("%d\n", n); // prints the value of the parameter } // scope of function parameter 'n' ends int a = n; // Error: name 'n' is not in scope
|
C99までは、選択文と反復文はそれ自身のブロック スコープを確立しませんでした(ただし、文に使用された複合文は通常のブロック スコープを持っていました)。 enum {a, b}; int different(void) { if (sizeof(enum {b, a}) != sizeof(int)) return a; // a == 1 return b; // b == 0 in C89, b == 1 in C99 } |
(C99以降) |
ブロック スコープの変数は、デフォルトでリンケージを持たず、自動記憶域期間を持ちます。VLAでないローカル変数の記憶域期間はブロックに入ると開始しますが、宣言が見られるまで変数はスコープ内になく、アクセスできません。
[編集] ファイル スコープ
どのブロックまたはパラメータリストの外側で宣言された識別子のスコープは、宣言点から始まり、翻訳単位の終端で終了します。
int i; // scope of i begins static int g(int a) { return a; } // scope of g begins (note, "a" has block scope) int main(void) { i = g(2); // i and g are in scope }
ファイル スコープの識別子は、デフォルトで外部リンケージと静的記憶域期間を持ちます。
[編集] 関数 スコープ
関数内で宣言されたラベル(ラベルのみ)は、その関数のどこでも、すべてのネストしたブロックで、その宣言の前でも後でもスコープ内にあります。注:ラベルは、コロン文字の前に、それ以外では使用されていない識別子を使用して、いずれかのステートメントの前に現れることで暗黙的に宣言されます。
void f() { { goto label; // label in scope even though declared later label:; } goto label; // label ignores block scope } void g() { goto label; // error: label not in scope in g() }
[編集] 関数プロトタイプ スコープ
関数の定義ではない関数宣言のパラメータリストで導入された名前のスコープは、関数宣言子の終端で終了します。
int f(int n, int a[n]); // n is in scope and refers to the first parameter
宣言に複数の宣言子またはネストした宣言子がある場合、スコープは最も近い外側の関数宣言子の終端で終了することに注意してください。
void f ( // function name 'f' is at file scope long double f, // the identifier 'f' is now in scope, file-scope 'f' is hidden char (**a)[10 * sizeof f] // 'f' refers to the first parameter, which is in scope ); enum{ n = 3 }; int (*(*g)(int n))[n]; // the scope of the function parameter 'n' // ends at the end of its function declarator // in the array declarator, global n is in scope // (this declares a pointer to function returning a pointer to an array of 3 int)
[編集] 宣言点
構造体、共用体、列挙型のタグのスコープは、タグを宣言する型指定子でのタグの出現直後に始まります。
struct Node { struct Node* next; // Node is in scope and refers to this struct };
列挙定数のスコープは、列挙子リストでの定義された列挙子の出現直後に始まります。
enum { x = 12 }; { enum { x = x + 1, // new x is not in scope until the comma, x is initialized to 13 y = x + 1 // the new enumerator x is now in scope, y is initialized to 14 }; }
その他の識別子のスコープは、宣言子の終了直後、および(もしあれば)初期化子の前に始まります。
int x = 2; // scope of the first 'x' begins { int x[x]; // scope of the newly declared x begins after the declarator (x[x]). // Within the declarator, the outer 'x' is still in scope. // This declares a VLA array of 2 int. }
unsigned char x = 32; // scope of the outer 'x' begins { unsigned char x = x; // scope of the inner 'x' begins before the initializer (= x) // this does not initialize the inner 'x' with the value 32, // this initializes the inner 'x' with its own, indeterminate, value } unsigned long factorial(unsigned long n) // declarator ends, 'factorial' is in scope from this point { return n<2 ? 1 : n*factorial(n-1); // recursive call }
特別なケースとして、識別子の宣言ではない型名のスコープは、型名内で識別子が表示されるであろう場所の直後から始まるとみなされます。
[編集] 注記
C89以前は、外部リンケージを持つ識別子はブロック内で導入された場合でもファイル スコープを持っていました。そのため、C89コンパイラはスコープから外れた`extern`識別子の使用を診断する必要はありません(そのような使用は未定義の動作です)。
Cでは、`for`ループの初期化句で宣言された変数を、ループ本体内のローカル変数が隠すことができます(それらのスコープはネストしています)が、C++ではそれができません。
C++とは異なり、Cには構造体スコープがありません。構造体/共用体/列挙型宣言内で宣言された名前は、構造体宣言と同じスコープにあります(ただし、データメンバーは独自のメンバー名前空間にあります)。
struct foo { struct baz {}; enum color {RED, BLUE}; }; struct baz b; // baz is in scope enum color x = RED; // color and RED are in scope
[編集] 参考文献
- C23標準 (ISO/IEC 9899:2024)
- 6.2.1 識別子、型名、および複合リテラルのスコープ (p: TBD)
- C17標準 (ISO/IEC 9899:2018)
- 6.2.1 識別子のスコープ (p: 28-29)
- C11標準 (ISO/IEC 9899:2011)
- 6.2.1 識別子のスコープ (p: 35-36)
- C99標準 (ISO/IEC 9899:1999)
- 6.2.1 識別子のスコープ (p: 29-30)
- C89/C90標準 (ISO/IEC 9899:1990)
- 3.1.2.1 識別子のスコープ
[編集] 関連項目
| C++ のドキュメント (スコープ)
|