一、參數與通用函式設計原則
- 避免全域變數:通用函式應透過參數接收所有資料,降低耦合與副作用。
- 參數傳遞機制:
- 傳值:基本型別、小型結構體 (≤ 16 bytes) → 複製成本低。
- 傳址 (指標):大型結構體、陣列、需修改呼叫端變數、避免複製開銷。
- 傳遞
const 指標:唯讀大型物件,兼具效能與安全。
- 回傳值:單一值用回傳;多值用輸出參數 (指標) 或結構體。
二、效能考量:函式呼叫開銷 vs 內聯
| 開銷來源 | 說明 | 典型週期 |
| 呼叫/返回指令 | call/ret、堆疊框架建立/銷毀 | 10-30 cycles |
| 參數壓棧/彈出 | 呼叫慣例 (cdecl, stdcall, fastcall) 差異 | 視參數數量 |
| 暫存器保存/還原 | 被呼叫者保存暫存器 | ~5-15 cycles |
內聯策略
static inline (C99) / __inline (MSVC) / inline (C++):建議編譯器展開,最終由最佳化器決定。
- 適用場景:極小、高頻呼叫的函式 (如
abs, max, 位元操作)。
- 副作用:程式碼膨脹 → 指令快取未命中增加,反而降低效能。
- 遞迴函式不可內聯 (編譯器會忽略)。
範例:平方函式內聯對比
// 內聯版本
static inline int sqr(int x) { return x * x; }
// 非內聯版本
int sqr_func(int x) { return x * x; }
int main() {
for (int i = 0; i < 10; i++) {
int a = sqr(i); // 編譯器可能展開為 i*i
int b = sqr_func(i); // 保證函式呼叫
}
}
三、遞迴函式設計指南
- 終止條件:明確、必達,避免無限遞迴導致堆疊溢位。
- 尾端遞迴優化 (TCO):GCC/Clang `-O2` 可優化尾端遞迴為迴圈;MSVC 需 `/O2` 且簡單情況下生效。
- 堆疊深度估算:每層堆疊框架 ~32-64 bytes,預設 1-8 MB 堆疊可支援數萬層,但大型區域變數會大幅減少。
- 迴圈替代:深度不可控時,改用顯式堆疊 (
malloc 陣列) 實作迭代版本。
四、函式指標與回呼模式
// 型別定義
typedef int (*CmpFunc)(const void*, const void*);
// 呼叫端
void sort(void* base, size_t n, size_t sz, CmpFunc cmp) {
// ... qsort 核心邏輯 ...
if (cmp(elem1, elem2) > 0) swap(...);
}
// 實作比較函式
int cmp_int(const void* a, const void* b) {
return *(const int*)a - *(const int*)b;
}
// 使用
int arr[] = {5,2,8,1};
sort(arr, 4, sizeof(int), cmp_int);
五、常見錯誤與最佳實務
| 錯誤 | 後果 | 修正 |
| 函式未宣告直接使用 | 隱式宣告 `int func()`,參數檢查失效 | 統一放入標頭檔,`#include` 引用 |
| 大型結構體傳值 | 堆疊複製開銷大,效能差 | 改傳 `const struct Big*` |
| 指標參數未檢查 NULL | 解參考導致 Crash | 進入函式首行 `if (!ptr) return ERROR;` |
| 遞迴無終止條件 | Stack Overflow | 加入 `if (depth > MAX) return;` |
| 忽略編譯器警告 | 潛在 Bug 流入生產 | `-Wall -Wextra -Werror` (GCC/Clang) 或 `/W4 /WX` (MSVC) |
結語
C 語言函式設計的核心在於明確介面契約 (參數、回傳、副作用)、權衡呼叫開銷與程式碼大小 (內聯判斷)、安全使用指標與遞迴。遵循「單一職責、參數化、最小驚訝」原則,配合編譯器最佳化與靜態分析,能寫出高效、可維護的系統級程式碼。