指针能指向变量,凭什么不能指向函数?函数指针就是用指针去指一个函数,然后通过指针来调用它。绕这么一圈有什么用,看完就明白。
为什么要用函数指针
你会问:直接 函数名() 调用不就行了?确实行,但当你需要把选哪个函数这件事留给程序运行时的其他代码决定时,函数指针就是答案。经典例子是排序:同一个数组,怎么排取决于要不要倒序;用函数指针,比较逻辑做成一个参数传进去就行。
声明语法:从里往外读
函数指针的声明看着吓人,其实按顺序拆就好。先看最简单的:
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 那个例子就算入门了。