首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言函數指針與指針函數深度解析:聲明/調用/回調/表驅動實戰

     編輯:關於C語言

C語言函數指針與指針函數深度解析:聲明/調用/回調/表驅動實戰

函數指針與指針函數是 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;
}

關鍵點:返回指針指向的內存必須在函數返回後仍有效(靜態/全局/堆內存),絕不能返回局部變量地址。

函數指針四大實戰模式

1. 回調函數

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 ...

2. 狀態機 / 策略模式

typedef enum {IDLE, RUN, STOP} State;
void IdleHandler() { ... }
void RunHandler() { ... }
void StopHandler() { ... }

void (*stateHandlers[3])() = {IdleHandler, RunHandler, StopHandler};

void Dispatch(State s) { stateHandlers[s](); }

3. 表驅動排序/查找(標準庫 qsort 模式)

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);  // 字符串排序

4. 信號處理 / 異步事件

void SigHandler(int sig) { ... }
signal(SIGINT, SigHandler);  // 註冊函數指針作為信號處理器

標準庫 qsort 深度解析

void qsort(void* base, size_t nmemb, size_t size,
           int (*compar)(const void*, const void*));
// compar: 函數指針,比較兩個元素(通過void*泛型指針)
// 返回:<0 前者小, =0 相等, >0 前者大

常見錯誤與避坑

  1. 聲明混淆:int *pf(int) 是指針函數,int (*pf)(int) 是函數指針
  2. 返回局部地址:指針函數返回棧上局部變量地址 → 未定義行為
  3. 參數類型不匹配:函數指針聲明參數與實際函數不一致 → 調用時棧破壞
  4. 調用約定不一致:Windows __stdcall vs __cdecl 混用 → 棧不平衡
  5. 空指針調用:未初始化/賦值 NULL 的函數指針直接調用 → 崩潰

總結對比表

維度函數指針指針函數
本質指針變量函數
聲明標記(*name) 被括號包裹*name() * 在函數名左、括號外
sizeof指針大小 (4/8字節)函數代碼大小
賦值對象函數名/同類型指針無(調用返回指針)
典型用途回調、策略、qsort、異步工廠、查找、封裝分配器
右左法則關鍵標識符右邊先遇到 () 參數列表標識符右邊先遇到 () 調用運算符

總結

函數指針將「函數」作為一級數據對象傳遞、存儲、延遲調用,是 C 語言實現多態、回調、泛型算法的核心機制;指針函數則封裝「指針創建/查找」邏輯,返回內存地址供調用者直接訪問。掌握 聲明語法(括號位置決定本質)、右左法則解析複雜類型、四大實戰模式(回調/狀態機/表驅動/異步),即可靈活運用這對「孿生概念」構建靈活可擴展的 C 系統架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved