儲存類別指定符:決定變數「活多久、誰能看見、放哪裡」
Turbo C 2.0 (ANSI C89) 定義四種儲存類別指定符,互斥 (一個宣告僅能出現一個)。
1. auto — 隱式預設,極少顯式書寫
- 僅能用於區塊作用域變數 (函數內部)。
- 語意:自動儲存期,進入區塊配置,離開釋放。
- 所有無儲存類別指定符的區塊內變數隱含為
auto。
- 無法用於檔案作用域或參數宣告。
2. static — 雙重身分,靜態儲存期
| 語境 | 名稱 | 可見度 | 初始化 | 生命週期 |
| 區塊內 | 靜態區域變數 | 僅該區塊 | 程式啟動前一次性初始化 (預設 0) | 程式全程 |
| 檔案層級 | 靜態全域變數 / 內部連結函數 | 僅本轉譯單元 (檔案) | 程式啟動前一次性初始化 (預設 0) | 程式全程 |
靜態區域變數經典用法:函數呼叫計數器
int counter(void)
{
static int cnt = 0; /* 僅初始化一次 */
return ++cnt;
}
靜態全域變數:模組私有狀態
/* file1.c */
static int module_state = 0; /* 其他 .c 檔不可見 */
void module_init(void) { module_state = 1; }
int module_get_state(void) { return module_state; }
3. extern — 跨檔案共享的橋樑
- 宣告 (非定義):告訴編譯器「此識別符在別處定義,型別為 X」。
- 不配置儲存空間,僅建立符號表項目。
- 配合標頭檔
.h 實現外部連結。
跨檔案共享範例
/* globals.h */
extern int g_counter; /* 宣告 */
extern void g_init(void);
/* globals.c */
int g_counter = 0; /* 定義 (配置空間) */
void g_init(void) { g_counter = 42; }
/* main.c */
#include "globals.h"
int main(void) { g_init(); printf("%d\n", g_counter); }
4. register — 請求暫存器配置 (僅建議)
- 僅適用於區塊作用域的
int、char、指標等機器字長以內型別。
- 語意:建議編譯器盡量放入 CPU 暫存器,加速存取。
- 不可取址:
®_var 為非法 (因暫存器無位址)。
- 編譯器可忽略請求 (暫存器不足時退回記憶體),Turbo C 2.0 限制 同時最多 2 個。
- 現代編譯器 (GCC/Clang/MSVC) 完全自動分配暫存器,
register 關鍵字僅保留相容性,不建議手動指定。
儲存類別快速決策表
| 需求 | 選擇 | 範例 |
| 函數內臨時變數 | (無指定符 / auto) | int i; |
| 函數內保持狀態 | static | static int call_cnt; |
| 模組私有全域狀態 | static (檔案層級) | static int mod_state; |
| 專案級共享全域狀態 | extern 宣告 + 單一定義 | extern int g_cfg; |
| 極度熱點變數 (舊編譯器) | register | register int idx; |
現代 C 實務建議
- 棄用
register:編譯器優化器比人工更懂暫存器分配。
- 善用
static 封裝:預設檔案層級識別符皆加 static,僅透過標頭檔 extern 公開必要介面。
- 全域變數極小化:傾向「模組狀態結構體 + 實例指標」模式,而非散落的
extern 變數。
- 啟用
-fvisibility=hidden (GCC/Clang):預設隱藏符號,配合 __attribute__((visibility("default"))) 公開 API,實現更細粒度控制。