存儲類別指示符 (auto/static/extern/register) 決定變量的三大屬性:
| 類別 | 關鍵字 | 存儲位置 | 生命周期 | 作用域 | 默認初始值 | 典型用途 |
|---|---|---|---|---|---|---|
| 自動 | auto (可省略) | 棧 | 塊/函數執行期間 | 塊/函數內部 | 不定值 (垃圾值) | 局部變量、函數參數 (99%情況) |
| 靜態 | static | 數據段 (BSS/數據區) | 程序整個運行期間 | 見下文分類 | 0 / NULL | 保持狀態、文件級封裝、單例模式 |
| 外部 | extern | 數據段 | 程序整個運行期間 | 全項目 (跨文件) | 0 / NULL | 跨文件共享全局變量/函數 |
| 寄存器 | register | CPU 寄存器 (或內存) | 塊/函數執行期間 | 塊/函數內部 | 不定值 | 極高頻訪問的局部變量/參數 |
static)
void counter() {
static int count = 0; // 僅初始化一次!
printf("%d ", ++count);
}
int main() {
counter(); // 1
counter(); // 2
counter(); // 3 → 保持上次值
}
static)
// file1.c
static int secret = 42; // 僅 file1 可見
static void internal_init() {} // 僅 file1 可調用
// file2.c
// extern int secret; // ❌ 鏈接錯誤:找不到符號
// external_init(); // ❌ 未聲明
static 的全局變量/函數默認 extern (External Linkage),全項目可見
// 定義文件:file1.c (僅定義一次,分配存儲)
int global_counter = 0;
void increment() { global_counter++; }
// 聲明文件:file1.h (供其他文件包含)
extern int global_counter; // 告訴編譯器:此變量在別處定義
extern void increment(); // 函數 extern 可省略
// 使用文件:file2.c
#include "file1.h"
void use_it() {
printf("%d\n", global_counter); // 訪問 file1 定義的變量
increment();
}
extern 只聲明不定義 (不分配內存)extern "C" 防止名字修飾int/char/short/指針 (機器字長內)register 變量取地址 (® 編譯錯誤)現代編譯器 (GCC/Clang/MSVC -O2/-O3) 擁有強大的圖著色寄存器分配器,自動將熱變量分配到寄存器。**手動 register 往往干擾編譯器優化**,現代 C 編程**極少使用**,僅在嵌入式裸機、內核代碼、極端性能敏感路徑作微調。
file.prj
file1.c
file2.c
file.prjfile.exe
gcc -c file1.c -o file1.o
gcc -c file2.c -o file2.o
gcc file1.o file2.o -o program
# 或一步到位:
gcc file1.c file2.c -o program
| 場景 | 推薦存儲類別 | 理由 |
|---|---|---|
| 普通局部變量 | auto (省略) | 棧分配、自動管理、無副作用 |
| 函數間保持狀態 | static (局部) | 生命周期全程、作用域局部、線程不安全注意 |
| 模塊私有全局狀態 | static (文件級) | 封裝、防符號衝突、C語言模塊化基石 |
| 跨模塊共享數據 | extern (聲明) + 定義 | 標準共享機制、頭文件聲明、單一定義 |
| 極熱路徑變量 (現代) | 無 (依賴編譯器 -O2) | 編譯器比人工更懂寄存器分配 |
| 極熱路徑變量 (嵌入式/內核) | register | 明確請求、無地址、配合汇編檢查 |
理解存儲類別,不僅是語法考點,更是**內存模型、模塊化設計、性能優化**的基石。請結合具體項目場景,反覆體會 static 的雙重語義與 extern 的聲明定義分離原則!