函數指標本質是一個變量,存儲函數入口地址。它讓「函數」成為一等公民:可作為參數傳遞、存入數組、動態綁定行為。語法形式:
返回類型 (*指標名)(參數列表);
// 例:int (*pf)(int, int); // 指向「接受兩個int、返回int」的函數
int Sum(int a, int b) { return a + b; }
int Sub(int a, int b) { return a - b; }
int main()
{
int (*pf)(int, int); // 聲明
pf = Sum; // 賦值:函數名隱式轉換為地址
printf("Sum: %d\n", pf(20, 10)); // 30
pf = Sub; // 運行時切換行為
printf("Sub: %d\n", pf(20, 10)); // 10
return 0;
}
核心優勢:同一調用點 pf(a,b),行為由運行時賦值決定,實現策略模式的 C 語言原生形態。
/* 定義函數指標類型別名,提升可讀性 */
typedef int (*BinaryOp)(int, int);
/* 高階函數:接收操作邏輯作參數 */
int Calculate(int a, int b, BinaryOp op)
{
return op(a, b); // 間接調用
}
int main()
{
printf("%d\n", Calculate(20, 10, Sum)); // 30
printf("%d\n", Calculate(20, 10, Sub)); // 10
return 0;
}
這正是回調函數的教科書定義:Calculate 不關心具体運算邏輯,將控制權「回調」給傳入的函數指標。廣泛應用於:
int (*compar)(const void*, const void*)// 無 typedef:參數聲明冗長難讀
void RegisterCallback(int (*cb)(int, int));
// 有 typedef:語義清晰
typedef int (*EventHandler)(int eventId, void* ctx);
void RegisterCallback(EventHandler cb);
int SafeCalculate(int a, int b, BinaryOp op)
{
if (!op) return INT_MIN; // 或設置 errno
return op(a, b);
}
typedef void (*WorkerFunc)(void* context, int data);
void RunWorker(WorkerFunc func, void* ctx, int data)
{
if (func) func(ctx, data); // ctx 攜帶閉包數據
}
/* 使用例:計數器閉包 */
typedef struct { int count; } CounterCtx;
void CounterWorker(void* ctx, int data)
{
CounterCtx* c = (CounterCtx*)ctx;
c->count += data;
printf("Count: %d\n", c->count);
}
int main()
{
CounterCtx ctx = {0};
RunWorker(CounterWorker, &ctx, 5); // Count: 5
RunWorker(CounterWorker, &ctx, 3); // Count: 8
}
| 陷阱 | 現象 | 對策 |
|---|---|---|
| 聲明漏寫 * | int pf(int,int) 被解析為函數聲明而非指標 | 永遠寫 int (*pf)(int,int) |
| 調用約定不匹配 | 棧平衡錯誤、參數亂碼、崩潰 | 統一項目約定,或用 __stdcall 顯式標註 |
| 指向不同簽名函數 | 編譯警告,運行時棧損壞 | 嚴格匹配參數/返回類型 |
| 調試時無法設置斷點 | 間接調用 pf() 符號解析困難 | Watch 窗口查看 pf 地址,或在目標函數入口斷點 |
函數指標是 C 語言實現多態、依賴注入、異步回調的基石。掌握其聲明語法、類型兼容規則、上下文傳遞模式,能讓你編寫出高內聚、低耦合、可測試、可擴展的系統級代碼。從 qsort 到嵌入式狀態機,從 GUI 事件循環到驅動程序中斷處理,函數指標無處不在 —— 它是 C 語言「函數式編程」能力的核心體現。