指针能指向变量,凭什么不能指向函数?函数指针就是用指针去指一个函数,然后通过指针来调用它。绕这么一圈有什么用,看完就明白。

为什么要用函数指针

你会问:直接 函数名() 调用不就行了?确实行,但当你需要把选哪个函数这件事留给程序运行时的其他代码决定时,函数指针就是答案。经典例子是排序:同一个数组,怎么排取决于要不要倒序;用函数指针,比较逻辑做成一个参数传进去就行。

声明语法:从里往外读

函数指针的声明看着吓人,其实按顺序拆就好。先看最简单的:

void (*pf)(int);

从里往外读:

(*pf)    ,pf 是一个指针
(*pf)(int) ,它指向一个接收 int 的函数
void       ,那个函数的返回类型是 void

*pf 两旁的括号不能省,没了它就成了"返回 void* 的函数"的声明,完全不是一回事。进阶版同理:

char* (*pf)(int*);
/* 1. pf 是函数指针
   2. 它指向的函数返回 char*
   3. 该函数接收一个 int* 参数 */

赋值与调用

获取函数地址可以加 & 也可以不加(函数名就是地址,通常直接写名字):

#include <stdio.h>

void someFunction(int arg) {
    printf("someFunction 被调用了,参数是 %d\n", arg);
    printf("这个函数马上要返回了。\n");
}

int main() {
    void (*pf)(int);        /* 声明指针 */
    pf = &someFunction;      /* 或 pf = someFunction; */

    printf("我们要用指针调用 someFunction:\n");
    pf(5);                  /* 通过指针调用,(pf)(5) 也行 */
    printf("回到 main。\n");
    return 0;
}

回击经典:用函数指针写 qsort 倒序

标准库的 qsort 接受一个比较函数作为参数,比较逻辑完全由你定。下面传一个自定义比较函数实现降序排序:

#include <stdio.h>
#include <stdlib.h>   /* qsort */

/* 自定义比较函数:降序 */
int compare(const void *left, const void *right) {
    return *(int *)right - *(int *)left;
}

int main() {
    int cmp_result;  /* 留白,下面直接放进 qsort */
    int iarray[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int n = sizeof(iarray) / sizeof(iarray[0]);

    /* 传函数指针进去,让它按 compare 的规则排序 */
    qsort(iarray, n, sizeof(int), compare);

    for (int i = 0; i < n; i++) {
        printf("%d ", iarray[i]);   /* 9 8 7 6 5 4 3 2 1 */
    }
    printf("\n");
    return 0;
}

compare 换成另一个函数,排序方向立刻变,这就是函数指针带来的灵活性:同一套排序代码适配不同的排序规则,库函数不用为你重写。

数组的数组:函数指针表

函数指针也能装进数组,按索引挑函数来调,像菜单、状态机常用这套。下面拿三个函数练手:

#include <stdio.h>

void f1(int var) { printf("这是 f1,参数 %d\n", var); }
void f2(int var) { printf("这是 f2,参数 %d\n", var); }
void f3(int var) { printf("这是 f3,参数 %d\n", var); }

int main() {
    /* 函数指针数组:三个"接收 int、返回 void"的函数 */
    void (*funcs[3])(int) = {f1, f2, f3};

    for (int c = 0; c < 3; c++) {
        funcs[c](c);   /* 用下标挑函数来调用 */
    }
    return 0;
}

什么时候用函数指针

  • 给库函数传行为参数(qsort、bsearch 这类)。
  • 实现回调(callback):事件发生才调你给的函数。
  • 状态机、菜单、命令表:一个数组存一堆要挨个试的函数。

函数指针属于进阶内容,能看懂 qsort 那个例子就算入门了。