變量的存儲類別決定了其**生命週期**(何時分銷毀)、**作用域**(可見範圍)與**鏈接屬性**(跨檔案可見性)。本文透過三個對比實驗,徹底釐清區域變量、靜態區域變量、全域靜態變量的行為差異。
#include <stdio.h>
#include <stdlib.h>
void fun_global_static() {
int i = 0;
static int num = 0; // 靜態區域變量(實為全域存儲)
for (i = 0; i < 10; i++) {
num = 0; // 每次迴圈都歸零!邏輯錯誤
num++;
printf("%d ", num);
}
}
// 輸出:1 1 1 1 1 1 1 1 1 1
問題:static int num = 0 僅初始化一次,但迴圈內 num = 0 每次執行都重置,導致永遠輸出 1。
void fun_local() {
int i = 0;
for (i = 0; i < 10; i++) {
int num = 0; // 每次迴圈進入重新分配、初始化
num++;
printf("%d ", num);
}
}
// 輸出:1 1 1 1 1 1 1 1 1 1
區域變量在**塊作用域**內,每次進入塊重新創建,離開塊銷毀。迴圈體是一個塊,故每次迴圈都重新初始化。
void fun_static_local() {
int i = 0;
for (i = 0; i < 10; i++) {
static int num = 0; // 僅第一次初始化,保持值
num++;
printf("%d ", num);
}
}
// 輸出:1 2 3 4 5 6 7 8 9 10
關鍵差異:static 將變量存儲提升至**靜態存儲區**(與全域變量同區),生命週期延長至程式結束,但作用域仍限於函數內部。
| 屬性 | 區域變量 (auto) | 靜態區域變量 (static) | 全域變量 | 靜態全域變量 (static) |
|---|---|---|---|---|
| 存儲區 | 棧 | 靜態區 | 靜態區 | 靜態區 |
| 生命週期 | 函數調用期間 | 程式整個運行期 | 程式整個運行期 | 程式整個運行期 |
| 作用域 | 塊內部 | 塊內部 | 全檔案(外部鏈接) | 當前檔案(內部鏈接) |
| 預設初值 | 垃圾值 | 0 | 0 | 0 |
| 初始化時機 | 每次進入塊 | 程式啟動前(僅一次) | 程式啟動前 | 程式啟動前 |
| 跨檔案可見 | 否 | 否 | 是 (extern) | 否 |
static 限制檔案內可見,減少耦合。高位址 +------------------+ | 棧區 (向下增長) | ← 區域變量、函數參數、返回位址 | ... | | 堆區 (向上增長) | ← malloc/free 管理 | ... | | BSS 段 | ← 未初始化全域/靜態變量 (預設 0) | DATA 段 | ← 已初始化全域/靜態變量 | 程式碼段 | ← 機器指令、常量字串 低位址
**區域變量**適合臨時計算,**靜態區域變量**適合函數內部保持狀態(如計數器、單例快取),**全域變量**慎用並加 static 限制作用域。理解存儲類別是掌握 C 語言記憶體模型、避免難以重現 Bug 的關鍵。