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

C語言指針函數與函數指針:區別、用法與高階應用

     編輯:關於C語言

名稱陷阱:一字之差,天壤之別

概念本質聲明語法核心用途
指針函數函數,返回指針type *func(args)返回動態內存、靜態數組元素地址、鏈表節點
函數指針指針,指向函數type (*funcPtr)(args)回調、多態、狀態機、插件架構、qsort 比較器

記憶口訣:「指針函數」中心詞是「函數;「函數指針」中心詞是「指針。

一、指針函數:返回地址的函數

1. 語法與語義

int *getPtr();        // 返回 int*
Node *createNode();   // 返回結構體指針
int (*getRow(int))[4]; // 返回指向 int[4] 的指針

2. 典型場景:查找二維數組元素

int calendar[5][7] = { /* ... */ };

// 返回指定周、日的元素地址
int *getDate(int week, int day) {
    if (week < 1 || week > 5 || day < 1 || day > 7) return NULL;
    return &calendar[week-1][day-1];
}

int main() {
    int w, d;
    scanf("%d%d", &w, &d);
    int *p = getDate(w, d);
    if (p) printf("Value: %d\n", *p);
    else puts("Invalid input");
    return 0;
}

3. 關鍵規則

  • 返回指針指向的內存必須在調用後仍有效:靜態變量、堆內存、全局/靜態數組。
  • ❌ 絕不返回局部變量地址(棧內存函數返回即失效)。
  • 返回 NULL 表示失敗/無效。

二、函數指針:將函數作為數據傳遞

1. 聲明與初始化

// 基本聲明
void (*funcPtr)(int, int); // 指向 void(int,int) 的指針

// typedef 簡化
typedef int (*CmpFunc)(const void*, const void*);
CmpFunc cmp = myCompare;

// 賦值:函數名隱式轉換為地址
void foo(int a) { printf("%d\n", a); }
void (*p)(int) = foo;      // &foo 同義
p(42);                     // 調用:(*p)(42) 或 p(42)

2. 核心應用:回調與策略模式

// 通用處理函數
void process(int *arr, int n, void (*op)(int*)) {
    for (int i = 0; i < n; i++) op(&arr[i]);
}

// 具體操作
void doubleVal(int *x) { *x *= 2; }
void squareVal(int *x) { *x *= *x; }

int main() {
    int a[] = {1,2,3,4,5};
    process(a, 5, doubleVal); // 2 4 6 8 10
    process(a, 5, squareVal); // 4 16 36 64 100
    return 0;
}

3. 標準庫經典:qsort 比較器

int cmpInt(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

int arr[] = {5,2,8,1,9};
qsort(arr, 5, sizeof(int), cmpInt); // 1 2 5 8 9

三、指針的指針:修改指針本身

當函數需要修改調用者的指針變量(如分配內存、鏈表頭插入),需傳遞指針的地址。

void allocate(int **ptr, int n) {
    *ptr = malloc(n * sizeof(int)); // 修改調用者的指針
}

int main() {
    int *p = NULL;
    allocate(&p, 10); // 傳遞 p 的地址
    // p 現在指向有效內存
    free(p);
    return 0;
}

多級指針解讀

char c = 'A';
char *p1 = &c;      // 一級指針
char **p2 = &p1;    // 二級指針
char ***p3 = &p2;   // 三級指針 (罕見)

*p2  == p1;         // 取得一級指針
**p2 == c;          // 取得最終數據

四、指向指針數組的指針:字符串數組處理

char *names[] = {"Alice", "Bob", "Charlie", NULL};

char **p = names;
while (*p) {
    printf("%s\n", *p++); // *p 取得字符串指針,p++ 移動到下一個
}

優勢:動態增減字符串,無需移動數據,僅調整指針數組。NULL 終結符簡化遍歷。

五、綜合實戰:插件式計算器

typedef double (*OpFunc)(double, double);

double add(double a, double b) { return a + b; }
double sub(double a, double b) { return a - b; }
double mul(double a, double b) { return a * b; }
double div(double a, double b) { return b ? a/b : 0; }

OpFunc ops[] = {add, sub, mul, div};
char *syms[] = {"+", "-", "*", "/"};

int main() {
    double x, y; char op;
    scanf("%lf %c %lf", &x, &op, &y);
    for (int i = 0; i < 4; i++) {
        if (op == syms[i][0]) {
            printf("%.2f\n", ops[i](x, y));
            break;
        }
    }
    return 0;
}

總結

  • 指針函數:工廠函數、查找函數的標準形式,注意生命週期管理。
  • 函數指針:實現回調、多態、動態分發的核心機制,配合 typedef 提升可讀性。
  • 多級指針:解決「函數內修改外部指針」問題,鏈表、樹、內存分配器必備。
  • 善用 NULL 終結的指針數組處理變長字符串列表。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved