函數指針與指針函數是 C 語言進階的分水嶺,前者是「指向函數的指針」,後者是「返回指針的函數」。本文從本質區分、聲明語法、調用方式、典型應用場景(回調、狀態機、表驅動)四個維度徹底講透,並提供標準庫 qsort、信號處理、菜單系統等實戰代碼模板。
int (*pf)(int, int);int* func(int, int);返回類型 (*指針名) (參數類型列表); // 例: int (*pfun)(int, int); // 指向:參數兩個int,返回int的函數
關鍵規則:括號 () 優先級高於 *,(*pfun) 先構成「指針」,再接參數列表成為「函數指針」。
int Max(int a, int b) { return a>b?a:b; }
int (*pfun)(int, int);
pfun = Max; // 函數名即地址,& 可省略
pfun = &Max; // 等效
// 調用兩種寫法等效:
int r1 = (*pfun)(3, 5); // 顯式解引用
int r2 = pfun(3, 5); // 隱式調用(C標準支持)
// int (*fun)(int *p) fun → 右邊 (int *p) → 函數,參數int* → 左邊 * → 指針 → 左邊 int → 返回int 結論:函數指針,指向 參數int* 返回int 的函數 // int (*fun[5])(int *p) fun → 右邊 [5] → 數組(5元素) → 左邊 * → 元素是指針 → 跳出 → 右邊 (int *p) → 函數 結論:函數指針數組,5個元素,每個指向 參數int* 返回int 的函數
返回類型 *函數名 (參數列表); // 等價:返回類型* 函數名(參數列表); // 例: int* GetDate(int week, int day); // 返回 int* 的函數
int* GetDate(int wk, int day) {
static int cal[5][7] = {
{1,2,3,4,5,6,7},
{8,9,10,11,12,13,14},
{15,16,17,18,19,20,21},
{22,23,24,25,26,27,28},
{29,30,31,-1}
};
return &cal[wk-1][day-1]; // 返回靜態數組元素地址
}
int main() {
int wk, day;
scanf("%d%d", &wk, &day);
printf("%d\n", *GetDate(wk, day)); // 解引用獲取值
return 0;
}
關鍵點:返回指針指向的內存必須在函數返回後仍有效(靜態/全局/堆內存),絕不能返回局部變量地址。
void Process(void (*callback)(int)) {
for (int i=0; i<10; i++) callback(i);
}
void Print(int x) { printf("%d ", x); }
void Square(int x) { printf("%d ", x*x); }
Process(Print); // 0 1 2 ...
Process(Square); // 0 1 4 9 ...
typedef enum {IDLE, RUN, STOP} State;
void IdleHandler() { ... }
void RunHandler() { ... }
void StopHandler() { ... }
void (*stateHandlers[3])() = {IdleHandler, RunHandler, StopHandler};
void Dispatch(State s) { stateHandlers[s](); }
int cmp_int(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int cmp_str(const void* a, const void* b) {
return strcmp(*(char**)a, *(char**)b);
}
qsort(arr, n, sizeof(int), cmp_int); // 整數排序
qsort(strs, n, sizeof(char*), cmp_str); // 字符串排序
void SigHandler(int sig) { ... }
signal(SIGINT, SigHandler); // 註冊函數指針作為信號處理器
void qsort(void* base, size_t nmemb, size_t size,
int (*compar)(const void*, const void*));
// compar: 函數指針,比較兩個元素(通過void*泛型指針)
// 返回:<0 前者小, =0 相等, >0 前者大
int *pf(int) 是指針函數,int (*pf)(int) 是函數指針__stdcall vs __cdecl 混用 → 棧不平衡| 維度 | 函數指針 | 指針函數 |
|---|---|---|
| 本質 | 指針變量 | 函數 |
| 聲明標記 | (*name) 被括號包裹 | *name() * 在函數名左、括號外 |
| sizeof | 指針大小 (4/8字節) | 函數代碼大小 |
| 賦值對象 | 函數名/同類型指針 | 無(調用返回指針) |
| 典型用途 | 回調、策略、qsort、異步 | 工廠、查找、封裝分配器 |
| 右左法則關鍵 | 標識符右邊先遇到 () 參數列表 | 標識符右邊先遇到 () 調用運算符 |
函數指針將「函數」作為一級數據對象傳遞、存儲、延遲調用,是 C 語言實現多態、回調、泛型算法的核心機制;指針函數則封裝「指針創建/查找」邏輯,返回內存地址供調用者直接訪問。掌握 聲明語法(括號位置決定本質)、右左法則解析複雜類型、四大實戰模式(回調/狀態機/表驅動/異步),即可靈活運用這對「孿生概念」構建靈活可擴展的 C 系統架構。