接口 (.h) 聲明「做什麼」,實現 (.c) 定義「怎麼做」。 客戶代碼只依賴接口,實現可替換、可複用、可獨立編譯測試。
// arith.h #ifndef ARITH_H #define ARITH_H // 前綴命名避免衝突:模塊名_函數名 extern int Arith_max(int x, int y); extern int Arith_min(int x, int y); extern int Arith_div(int x, int y); // 向下取整除法 extern int Arith_mod(int x, int y); // 對應餘數 extern int Arith_ceiling(int x, int y); // 向上取整除法 extern int Arith_floor(int x, int y); // 向下取整除法 #endif
// arith.c
#include "arith.h"
int Arith_max(int x, int y) { return x > y ? x : y; }
int Arith_min(int x, int y) { return x > y ? y : x; }
// 關鍵:C 標準未定義負數除法/取模行為,此處明確語義
// 向下取整:商向 -∞ 方向,餘數同除數符號
// 即:x = y * Arith_div(x,y) + Arith_mod(x,y), 0 <= Arith_mod < |y| (y>0)
int Arith_div(int x, int y) {
// 檢測編譯器行為:標準 C99 截斷向 0,這裡修正為向下取整
if (y == 0) return 0; // 或錯誤處理
int q = x / y;
int r = x % y;
// 符號不同且有餘數時,向下調整
if ((r != 0) && ((x < 0) != (y < 0))) q--;
return q;
}
int Arith_mod(int x, int y) {
if (y == 0) return 0;
int r = x % y;
if ((r != 0) && ((x < 0) != (y < 0))) r += y;
return r;
}
int Arith_floor(int x, int y) { return Arith_div(x, y); }
int Arith_ceiling(int x, int y) { return Arith_div(x, y) + (Arith_mod(x, y) != 0); }
// main.c #include#include "arith.h" int main() { printf("max(10,5)=%d\n", Arith_max(10,5)); printf("div(-13,5)=%d\n", Arith_div(-13,5)); // -3 (向下取整) printf("mod(-13,5)=%d\n", Arith_mod(-13,5)); // 2 return 0; }
// stack.h #ifndef STACK_H #define STACK_H typedef struct Stack_T* Stack_T; // 不完全類型,隱藏實現細節 extern Stack_T Stack_new(void); extern int Stack_empty(Stack_T stk); extern void Stack_push(Stack_T stk, void* x); extern void* Stack_pop(Stack_T stk); extern void Stack_free(Stack_T* stk); #endif
// stack.c #include#include #include "stack.h" #include "mem.h" // 假設提供 NEW/FREE 宏 struct Stack_T { int count; struct Elem { void* x; struct Elem* link; } *head; }; Stack_T Stack_new(void) { Stack_T stk; NEW(stk); // 宏:分配並檢查 stk->count = 0; stk->head = NULL; return stk; } int Stack_empty(Stack_T stk) { assert(stk); return stk->count == 0; } void Stack_push(Stack_T stk, void* x) { assert(stk); struct Elem* t; NEW(t); t->x = x; t->link = stk->head; stk->head = t; stk->count++; } void* Stack_pop(Stack_T stk) { assert(stk && stk->count > 0); struct Elem* t = stk->head; stk->head = t->link; stk->count--; void* x = t->x; FREE(t); return x; } void Stack_free(Stack_T* stk) { assert(stk && *stk); struct Elem *t, *u; for (t = (*stk)->head; t; t = u) { u = t->link; FREE(t); } FREE(*stk); }
頭文件僅聲明 typedef struct Stack_T* Stack_T;,結構體定義在 .c 中。客戶代碼無法直接訪問成員,**強制封裝**。
# 編譯實現 gcc -c arith.c -o arith.o gcc -c stack.c -o stack.o # 編譯客戶代碼 gcc -c main.c -o main.o # 鏈接 gcc main.o arith.o stack.o -o app # 或一步到位 gcc main.c arith.c stack.c -o app
| 特性 | 傳統單文件 | 接口/實現分離 |
|---|---|---|
| 編譯依賴 | 修改實現需重編所有客戶 | 只改 .c,客戶無需重編 |
| 多實現切換 | 困難 | 鏈接不同 .o 即可 |
| 團隊並行開發 | 衝突多 | 接口定穩後並行 |
| 封裝性 | 無 | 不透明指針強制封裝 |
| 測試 | 難隔離 | 可針對接口編寫測試 |
static inline 小函數_Generic 實現類多態__attribute__((cleanup)) 自動資源管理