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

C 語言函數指標與回呼機制:從語法到實戰模式

     編輯:關於C語言

一、函數指標語法完全解析

函數指標指向可執行程式碼的進入位址,宣告語法:

// 回傳型別 (*指標名稱) (參數列表)
int (*compare)(int a, int b);       // 指向 int func(int, int)
void (*callback)(const char *msg);  // 指向 void func(const char*)

關鍵技巧:使用 typedef 簡化複雜宣告:

typedef int (*CompareFunc)(int, int);
CompareFunc cmp = &myCompare; // 或直接 cmp = myCompare;

二、回呼函數核心模式

回呼 = 將函數指標作為參數傳遞給另一個函數,由被呼叫者在適當時機調用。實現「控制反轉」與演算法解耦。

經典應用場景

  • 排序比較器:qsort 使用 int (*compar)(const void*, const void*)。
  • 事件處理:GUI 框架註冊點擊、定時器回呼。
  • 泛型演算法:搜尋、遍歷、過濾邏輯外部化。

三、完整實戰範例:學員成績篩選與修飾

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char name[20];
    float score;
} Student;

// 回呼函數:修飾姓名
void decorateName(char *name) {
    strcat(name, "-高富帥");
}

// 泛型搜尋:分數超過門檻者執行回呼
void searchAndProcess(Student *stus, int count, float threshold, void (*processor)(char*)) {
    for (int i = 0; i < count; i++) {
        if (stus[i].score > threshold) {
            processor(stus[i].name);
        }
    }
}

// 輸入學員資料
Student *inputStudents(int n) {
    Student *arr = malloc(n * sizeof(Student));
    for (int i = 0; i < n; i++) {
        printf("第 %d 位學員姓名: ", i+1);
        scanf("%19s", arr[i].name);
        printf("分數: ");
        scanf("%f", &arr[i].score);
    }
    return arr;
}

void printStudents(Student *stus, int n) {
    puts("\n---------- 學員名單 ----------");
    for (int i = 0; i < n; i++)
        printf("%-10s %6.2f\n", stus[i].name, stus[i].score);
    puts("------------------------------\n");
}

int main() {
    Student *stus = inputStudents(3);
    printStudents(stus, 3);
    
    printf(">>> 篩選 90 分以上並修飾姓名 <<<\n");
    searchAndProcess(stus, 3, 90.0f, decorateName);
    
    printStudents(stus, 3);
    free(stus);
    return 0;
}

四、進階技巧:狀態攜帶與閉包模擬

C 無原生閉包,但可透過 void *context 傳遞狀態:

typedef void (*Action)(void *ctx, int item);

void forEach(int *arr, int n, Action act, void *ctx) {
    for (int i = 0; i < n; i++) act(ctx, arr[i]);
}

// 使用者自訂結構體攜帶狀態
typedef struct { int sum; int count; } StatsCtx;
void accumulate(void *ctx, int val) {
    StatsCtx *s = ctx;
    s->sum += val;
    s->count++;
}

int main() {
    int data[] = {1,2,3,4,5};
    StatsCtx ctx = {0};
    forEach(data, 5, accumulate, &ctx);
    printf("平均: %.2f\n", (float)ctx.sum / ctx.count);
}

五、函數指標陣列:跳躍表與狀態機

typedef void (*Handler)(void);
Handler handlers[] = {
    handleIdle,
    handleRun,
    handleJump,
    handleAttack
};

void dispatch(int state) {
    if (state >= 0 && state < 4) handlers[state]();
}

六、常見錯誤與避坑

  1. 參數型別不匹配:指標型別必須與目標函數簽名完全一致,否則未定義行為。
  2. 呼叫慣例差異:跨動態庫/編譯器時需注意 __cdecl/__stdcall 等呼叫慣例。
  3. NULL 指標解參考:調用前務必檢查 if (funcPtr) funcPtr();。
  4. 遞迴回呼導致堆疊溢位:深度遞迴改用迭代或顯式堆疊。

七、總結

函數指標是 C 語言實現多態、回呼、泛型演算法的核心機制。掌握 typedef 簡化宣告、void* context 傳遞狀態、函數指標陣列構建跳躍表三大模式,即可靈活應對各種系統級開發需求。

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