| 屬性 | 關鍵字 | 可見範圍 | 類比 |
| 外部連結 | extern | 整個專案(所有編譯單元) | Java public |
| 內部連結 | static | 僅本編譯單元(.c 檔) | Java private(檔案級) |
| 無連結 | auto (預設區域變數) | 僅所在區塊/函數 | 區域變數 |
外部連結 extern
變數:宣告非定義,告知編譯器「此變數在別處定義」。
// a.c
int g_val = 10; // 定義(配置記憶體)
// b.c
extern int g_val; // 宣告,使用 a.c 的 g_val
void foo() { printf("%d", g_val); }
函數:預設即為 extern,跨檔呼叫需在頭文件宣告。
// add.h
extern int add(int a, int b); // 關鍵字可省略
// add.c
int add(int a, int b) { return a + b; }
// main.c
#include "add.h"
int main() { printf("%d", add(1,2)); }
內部連結 static(檔案級)
修飾全域變數/函數,限制**僅本 .c 可見**,連結器不輸出符號,避免衝突。
// utils.c
static int internal_counter = 0; // 僅 utils.c 可見
static void internal_helper() { /* ... */ }
int public_api() { // 外部連結
internal_helper();
return ++internal_counter;
}
不同 .c 可定義同名 static 實體,互不干擾。
無連結 auto
區域變數預設儲存類別為 auto(C++11 後 auto 改為型別推導),**僅在宣告區塊內可見**,函數間不可存取。
實戰模組化模式:.h + .c 分離
// stack.h(介面)
#ifndef STACK_H
#define STACK_H
void push(int v);
int pop(void);
#endif
// stack.c(實作)
#include "stack.h"
#define MAX 100
static int buf[MAX]; // 內部連結,隱藏實現細節
static int top = 0; // 內部連結
void push(int v) { if (top < MAX) buf[top++] = v; }
int pop(void) { return (top > 0) ? buf[--top] : -1; }
// main.c
#include "stack.h"
int main() { push(1); push(2); printf("%d %d", pop(), pop()); }
常見誤區
- ❌
static 全域變數以為「常駐記憶體」→ 確實常駐,但**關鍵語意是內部連結**,而非儲存期(儲存期為靜態儲存期,extern 全域變數也是)。
- ❌ 頭文件定義
static 函數 → 每個包含該頭文件的 .c 產生獨立副本,程式碼膨脹且各自為政。
- ❌ 混淆「儲存類別」與「連結屬性」:
static 同時影響兩者(區域變數:靜態儲存期+無連結;全域:靜態儲存期+內部連結)。
總結
extern:跨檔共享,專案級公開介面。
static(檔案級):隱藏實現細節,防止符號衝突,模組化核心手段。
auto:區域變數,函數內部暫存。
- 良好的 C 專案:**頭文件只放
extern 宣告,.c 內部盡量用 static 封裝**。
- 上一篇文章: DIY less 命令實作總結(C 語言系統程式設計)
- 下一篇文章: C 語言階乘計算完整指南
Copyright ©
程式碼之家 All Rights Reserved