名稱空間
變體
操作

作用域

來自 cppreference.com
< 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 編譯器不需要診斷超出作用域的外部識別符號的使用(這種使用是未定義行為)。

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

[編輯] 參考

  • 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++ 文件關於作用域