命名空间
变体
操作

作用域

来自 cppreference.cn
< c‎ | language

每个 标识符 在 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

[编辑] 块作用域

复合语句(包括函数体)、或出现在 ifswitchforwhiledo-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 编译器不需要诊断超出作用域的 extern 标识符的使用(这种使用是未定义行为)。

循环体内的局部变量可以隐藏 C 语言 for 循环初始化子句中声明的变量(它们的作用域是嵌套的),但在 C++ 中不能这样做。

与 C++ 不同,C 语言没有结构体作用域:在 struct/union/enum 声明中声明的名称与结构体声明在同一作用域中(但数据成员在它们自己的成员命名空间中)

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 标识符、类型名称和复合字面量的作用域 (页码:待定)
  • C17 标准 (ISO/IEC 9899:2018)
  • 6.2.1 标识符的作用域 (页码:28-29)
  • C11 标准 (ISO/IEC 9899:2011)
  • 6.2.1 标识符的作用域 (页码:35-36)
  • C99 标准 (ISO/IEC 9899:1999)
  • 6.2.1 标识符的作用域 (页码:29-30)
  • C89/C90 标准 (ISO/IEC 9899:1990)
  • 3.1.2.1 标识符的作用域

[编辑] 参见