作用域
C 程序中出现的每个标识符只有在源代码的某些可能不连续的部分(称为其作用域)中才是可见的(即可以使用)。
在一个作用域内,一个标识符可以表示多个实体,但这只适用于当这些实体位于不同的命名空间中时。
C 有四种作用域:
- 块作用域
- 文件作用域
- 函数作用域
- 函数原型作用域
目录 |
[编辑] 嵌套作用域
如果两个具有相同标识符的不同实体同时处于作用域内,并且它们属于同一个命名空间,则这些作用域是嵌套的(不允许其他形式的作用域重叠),并且内部作用域中的声明会隐藏外部作用域中的声明。
// 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 起),或在函数定义的参数列表中声明的标识符的作用域,始于声明点,终于声明所在块或语句的末尾。
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 编译器不需要诊断超出作用域的外部标识符的使用(这种使用是未定义行为)。
C 语言中循环体内的局部变量可以隐藏在for循环的初始化子句中声明的变量(它们的作用域是嵌套的),但在 C++ 中不能这样做。
与 C++ 不同,C 没有结构体作用域:在 struct/union/enum 声明中声明的名称与 struct 声明处于同一作用域(除了数据成员位于它们自己的成员命名空间中)。
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