在 C 語言中,static 是一個極具特色的關鍵字,它根據使用位置的不同,呈現出截然不同的行為。掌握 static 的多重含義,不僅能幫助你寫出更安全、模組化的程式碼,更能加深對程式執行期記憶體模型的理解。本文將從作用域限制、存儲期管理、記憶體區劃三個維度,系統梳理 static 的核心用法。
當 static 修飾函數內部的區域變數時,該變數的生命週期延長為整個程式執行期間,但作用域仍僅限於該函數內部。這意味著:
void counter() {
static int count = 0; // 僅初始化一次
count++;
printf("呼叫次數: %d\n", count);
}
int main() {
counter(); // 輸出: 呼叫次數: 1
counter(); // 輸出: 呼叫次數: 2
counter(); // 輸出: 呼叫次數: 3
return 0;
}
在函數外部(全域範圍)使用 static 修飾變數或函數,會將其連結屬性從外部連結變為內部連結:
extern 引用。等同於「模組私有全域變數」。// file_a.c
static int module_state = 0; // 僅 file_a 可見
static void helper() { ... } // 僅 file_a 可呼叫
void public_api() {
helper();
module_state++;
}
理解 static 存儲類別,必須結合 C 程式執行時的記憶體分區:
| 記憶體區域 | 存放內容 | 管理方式 | 預設初值 |
|---|---|---|---|
| Code 區 (Text) | 機器指令、常量字串 | 唯讀,編譯時固定 | — |
| Data 區 (靜態存儲區) | 全域變數、static 變數、常量 | 程式啟動分配,結束釋放 | 未顯式初始化者為 0 |
| Heap 區 (堆) | malloc/new 動態分配 | 程式設計師手動管理 (free/delete) | 不定值 (垃圾值) |
| Stack 區 (棧) | 函數參數、返回位址、區域變數、暫存變數 | 編譯器自動管理 (進棧/出棧) | 不定值 |
關鍵結論:所有 static 變數(含函數內靜態區域變數、靜態全域變數)均存放於 Data 區,而非 Stack 區。這解釋了為何它們能跨函數呼叫保持值,且預設初值為 0。
Stack 區空間有限(Windows VC 預設 1MB~4MB,Linux 通常 8MB)。在函數內定義大型陣列或結構體,極易導致棧溢位:
// 錯誤示範:棧上分配 16 MB 陣列 (int 4 bytes * 4M)
void stack_overflow() {
int huge_array[4 * 1024 * 1024]; // 典型棧溢位
// ...
}
正確做法:改用 Heap 區動態分配,或宣告為 static / 全域變數(放入 Data 區):
// 方案 1:動態分配 (Heap)
void safe_heap() {
int *p = malloc(sizeof(int) * 4 * 1024 * 1024);
if (p) { /* 使用 */ free(p); }
}
// 方案 2:靜態存儲 (Data 區) - 生命週期贏全程
static int huge_array[4 * 1024 * 1024]; // 檔案作用域或函數內 static
int *p = (int*)7; *p = 8; // 強制寫入位址 0x7malloc 返回 NULL)、Stack 區溢位 (遞迴過深、大型區域變數)。static 限制全域變數/函數的可見範圍,降低模組耦合。static 區域變數,避免濫用全域變數。static、全域或動態分配。malloc/new 返回值的習慣,防止空指標解引用。static 不改變作用域規則(函數內仍受塊作用域限制),只改變存儲期與連結屬性。透過掌握 static 的多重語意與記憶體模型,你將能寫出更健壯、模組化程度更高的 C 程式。