首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Scope

每个出现在C程序中的标识符都是可见的(也就是说可以使用),只能在源代码的一些可能不连续的部分称为它的范围

在一个范围内,只有当实体位于不同的名称空间时,标识符才可以指定多个实体。

C有四种范围:

  • 块范围
  • 文件范围
  • 功能范围
  • 函数原型范围

嵌套范围

如果由同一标识符命名的两个不同实体同时在范围内,并且它们属于同一名称空间,则范围是嵌套的(不允许其他形式的范围重叠),并且出现在内部范围内的声明隐藏出现在外部范围中的声明:

代码语言:javascript
复制
// 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以后)中出现的任何表达式,声明或语句中,或者在参数列表中一个函数定义从声明的地方开始,并在声明块或声明结束时结束。

代码语言:javascript
复制
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不同(void){if(sizeof(enum {b,a})!= sizeof(int))返回a; // a == 1 return b; // C89中的b == 0,C99中的b == 1}

(自C99以来)

默认情况下,块范围变量没有链接和自动存储持续时间。请注意,非VLA局部变量的存储时间在输入块时开始,但直到看到声明,该变量不在范围内且无法访问。

文件范围

在任何块或参数列表之外声明的任何标识符的范围从声明点开始,并在翻译单元结束时结束。

代码语言:javascript
复制
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
}

文件范围标识符默认具有外部链接和静态存储持续时间。

功能范围

在函数中声明的标签(只有标签)在该函数中的任何位置,在所有嵌套块中,在它自己的声明之前和之后。注意:通过在任何语句之前的冒号字符之前使用未使用的标识符来隐式声明标签。

代码语言:javascript
复制
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()
}

函数原型范围

在函数声明的参数列表中引入的不是定义的名称的作用域结束于函数声明器的末尾。

代码语言:javascript
复制
int f(int n,
      int a[n]); // n is in scope and refers to the first parameter

请注意,如果声明中有多个或嵌套的声明符,则该作用域将在最近的封闭函数声明符的末尾结束:

代码语言:javascript
复制
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)

生,m声明

结构体,联合体和枚举标签的范围在标签出现在声明标签的类型说明符后立即开始。

代码语言:javascript
复制
struct Node {
   struct Node* next; // Node is in scope and refers to this struct
};

枚举常量的范围在枚举器列表中出现其定义枚举器后立即开始。

代码语言:javascript
复制
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
         };
}

任何其他标识符的范围都在其声明符结束后并在初始化程序之前(如果有)开始:

代码语言:javascript
复制
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循环的init子句中声明的变量(它们的作用域是嵌套的),但在C ++中不能这样做。

与C ++不同,C没有结构范围:在struct / union / enum声明中声明的名称与结构声明在同一个范围内(除了数据成员在它们自己的成员名称空间中):

代码语言:javascript
复制
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

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com