在 C 中,談到變數範圍(scope)涉及許多層次,可以談到很複雜,這邊先談談全域變數(Global variable)、區域變數(Local variable)與區塊變數(Block variable)。
全域變數是指直接宣告在(主)函式之外的變數,這個變數在整個程式之中都可見,例如:
const double PI = 3.14159;
doule area(double r) {
return r * r * PI;
}
int main(void) {
// .....
return 0;
}
在這個例子中,PI
這個變數可以在主函式 main
與函式 area
使用,全域變數最好只用來定義一些常數,或者是確實具有全域概念的變數,不應為了方便而草率地將變數設為全域變數,否則會發生名稱空間重疊等問題;全域變數的生命週期始於程式開始,終止於程式結束。
區域變數是指函式中宣告的變數,或是宣告在參數列的參數,範圍只在函式之內,例如在上例的 main
函式無法取用 area
函式的變數 r
,區域變數的生命週期始於函式執行,終止於函式執行完畢。
區塊變數是指宣告在某陳述區塊中的變數,例如 while
迴圈區塊,或是 for
迴圈區塊,例如下面的變數 i
在迴圈結束之後,就不再有效:
for(int i = 0; i < 100; i++) {
// ....
}
範圍大的變數與範圍小的變數同名狀況時,範圍小的變數會暫時覆蓋範圍大的變數,稱為變數覆蓋,例如:
int i = 10;
for(int i = 0; i < 100; i++) {
// ...
}
printf("%d\n", i);
這個程式最後顯示的 i
值是 10,執行迴圈時,迴圈中的 i
變數範圍覆蓋迴圈外的 i
變數;全域變數與區域變數同名時也是如此運作。
再來介紹 static
,這個關鍵字有兩種不同的概念,記憶體模式與連結的方式,依使用的場合而有所不同。
就記憶體模式而言,變數宣告時若加上 static
,執行時期會一直存在記憶體的固定位置,在不同 .c 檔案頂層定義的變數,即使沒有加上 static
,也是這種記憶體模式。
因此若在函式中宣告 static
變數,代表著就算函式執行完畢,變數也不會消失。例如:
#include <stdio.h>
void count();
int main(void) {
for(int i = 0; i < 10; i++) {
count();
}
return 0;
}
void count() {
static int c = 1;
printf("%d\n", c);
c++;
}
執行結果:
1
2
3
4
5
6
7
8
9
10
雖然變數 c
是在 count
函式宣告,但是函式結束後,變數仍然有效,直到程式執行結束時才消失,雖然變數一直存在,但由於範圍限於函式之中,函式外仍無法存取該變數。
就連結模式而言,得先來探討一下,定義在另一個 .c 檔案頂層範圍中的變數,可以直接拿來用嗎?預設是不可以的,然而,可以透過 extern
聲明變數會在其他地方定義,例如:
foo.c
double v = 1000;
// ... 其他定義
main.c
#include <stdio.h>
int main() {
extern double v;
printf("%f\n", v);
return 0;
}
在 main.c 中並沒有宣告 v
,只是以 extern
聲明 v
是在其他地方定義,編譯器會試著找出符合的 v
,結果在 foo.c 找到,因而會顯示結果為 1000,要注意的是,extern
聲明 v
在其他位置定義,因此不能與初始式使用。
#include <stdio.h>
int main() {
extern double v = 2000; // error, `v' has both `extern' and initializer
// ...
return 0;
}
若要設定 v
變數,必須在 extern
聲明之後:
#include <stdio.h>
int main() {
extern double v;
v = 2000;
// ...
return 0;
}
定義在 .c 檔案的函式,若非實作標頭檔中定義的函式原型,預設是不能在另一個 .c 中使用的,若要使用得用 extern
宣告,例如,若 a.c 中定義了 void foo() {...}
,main.c 要使用,必須用 extern
如下宣告:
extern void foo();
定義在一個 .c 中的名稱,可以只改為內部連結,也就是想表示它只用在該 .c 中,這時可以加上 static
,例如,若方才的 foo.c 中的 v
宣告為:
foo.c
static double v = 1000;
// ... 其他定義
那麼範疇就只侷限在 foo.c 中了,不會被 extern
拿來連結。
函式若使用 static
修飾,表示內部連結,不會被 extern
拿來連結,如果想將函式實作定義在 .h 檔案中,可以加上 static
修飾。例如:
// 定義在 .h 中
static void foo() {
...
}