函數指針數組將多個同簽名函數的入口地址存儲在數組中,通過下標直接調用,實現「數據驅動邏輯」。
int Sum(int a, int b) { return a + b; }
int Sub(int a, int b) { return a - b; }
int Mul(int a, int b) { return a * b; }
int Div(int a, int b) { return b ? a / b : 0; }
typedef int (*pfFun)(int, int);
int TestFun(int a, int b, pfFun pf) {
return pf(a, b);
}
int main() {
pfFun ops[] = {Sum, Sub, Mul, Div};
printf("%d\n", TestFun(20, 10, ops[0])); // 30
printf("%d\n", TestFun(20, 10, ops[1])); // 10
return 0;
}
typedef enum { ADD=0, SUB, MUL, DIV, OP_MAX } OpCode;
int CalcSwitch(int a, int b, OpCode op) {
switch (op) {
case ADD: return Sum(a, b);
case SUB: return Sub(a, b);
case MUL: return Mul(a, b);
case DIV: return Div(a, b);
default: return -1;
}
}
缺點:
• 每新增運算需修改 enum、switch、case
• 圈復雜度 V(G) = 分支數 + 1,隨運算增多線性增長
• 違反開閉原則(OCP)
int CalcTable(int a, int b, OpCode op) {
if (op >= OP_MAX) return -1;
static const pfFun table[OP_MAX] = {Sum, Sub, Mul, Div};
return table[op](a, b);
}
優勢:
• 新增運算:只需在 table 末尾追加函數指針,enum 自動擴展
• 圈復雜度 = 1(單一路徑)
• O(1) 查找,無分支預測失敗
• 邏輯與數據分離,利於單元測試
McCabe 圈復雜度 V(G) = E - N + 2P(邊 - 節點 + 2×連通分量)。直觀理解:判定節點數 + 1。
• if/else、while、for、case、catch、&&/|| 短路均計 1 分
• 建議單函數 V(G) ≤ 10,超過需重構
typedef struct {
OpCode code;
const char *name;
pfFun handler;
const char *desc;
} OpEntry;
static const OpEntry g_op_table[] = {
{ADD, "add", Sum, "加法"},
{SUB, "sub", Sub, "減法"},
{MUL, "mul", Mul, "乘法"},
{DIV, "div", Div, "除法"},
// 新增只需在這裡添加一行
};
int CalcByName(int a, int b, const char *name) {
for (size_t i = 0; i < sizeof(g_op_table)/sizeof(g_op_table[0]); i++)
if (strcmp(g_op_table[i].name, name) == 0)
return g_op_table[i].handler(a, b);
return -1;
}
進一步支持:
• 字符串名稱調度(如 RPC、命令行解析)
• 運行時動態註冊(插件架構)
• 元數據驅動文檔生成
| 場景 | 適合驅動表? | 說明 |
|---|---|---|
| 固定枚舉、同簽名、高頻調用 | ✅ 極佳 | 編譯期綁定,零開銷 |
| 需運行時動態增減 | ✅ 配合函數指針鏈表/哈希表 | 插件系統、熱加載 |
| 參數類型/個數不一致 | ❌ | 需統一為 void* + 上下文結構體 |
| 極少分支(≤3)且穩定 | ⚠ 可不必 | switch 更直觀 |