命名空间
变体
操作

范围

来自 cppreference.com
< c‎ | 语言

每个出现在 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 foo {
    struct baz {};
    enum color {RED, BLUE};
};
struct baz b; // baz is in scope
enum color x = RED; // color and RED are in scope

[编辑] 参考文献

  • 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++ 文档 关于 范围