C 语言中的函数指针
在 C 语言里,函数指针是一种存放函数入口地址的指针变量,它可以把函数当作参数传递,也能在运行时动态调用。这一特性在回调函数、事件驱动程序以及"多态"(同一函数或操作在不同上下文中表现出不同行为)等场景中尤为实用。
示例代码:
c
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
// 声明一个与 add() 签名一致的函数指针
int (*fptr)(int, int);
// 将 add() 的地址赋给指针
fptr = &add;
// 通过指针调用函数并打印结果
printf("%d", fptr(10, 5));
return 0;
}
输出
15
解释:程序先定义函数 add(),随后将其地址赋给函数指针 fptr,最后通过该指针调用函数并打印两个整数的和。
函数指针的声明
函数指针必须按照它所指向函数的签名来声明。通用语法如下:
c
return_type (*pointer_name)(parameter_types);
return_type:函数返回值的类型parameter_types:函数参数的类型列表pointer_name:函数指针的名字
注意 :*pointer_name 必须放在括号里,否则编译器会把它解释为返回 return_type * 的普通函数声明,而不是指针。
函数的类型由"返回类型"以及"参数的个数和类型"共同决定,因此声明函数指针时,必须保证它的签名与后续要指向的函数完全一致。
例如上面的代码中,函数指针声明为:
c
int (*fptr)(int, int);
就与随后指向的 add() 函数签名完全匹配。
初始化
函数指针通过"把函数地址赋给它"来完成初始化:
c
pointer_name = &function_name;
也可以省略取地址符,因为函数名在表达式里本身就相当于一个常量函数指针:
c
pointer_name = function_name;
必须保证被赋值的函数签名(返回类型、参数列表)与指针声明时完全一致,否则编译器会报类型不匹配错误。
函数指针的特性
由于函数指针指向的是代码段中的指令地址,而非数据,因此它和普通指针相比有一些限制。主要特性如下:
- 指向代码段中函数的入口地址
- 要求函数签名(返回类型 + 参数列表)完全匹配
- 同一指针可先后指向多个签名一致的函数
- 不能进行自增、自减等指针算术运算
- 可用来构造"函数指针数组",实现跳转表(jump-table)等高级用法
应用与示例
下面通过代码示例展示函数指针的几种常见用法:
1. 作为参数传递(回调函数)
函数指针最有价值的用途之一,就是把函数当作参数传给另一个函数,从而在运行时决定具体调用哪个函数。
c
#include <stdio.h>
/* 普通加法 */
int add(int a, int b) {
return a + b;
}
/* 普通减法 */
int subtract(int a, int b) {
return a - b;
}
/* calc 接收一个函数指针 op,
用它对 a、b 做运算并打印结果 */
void calc(int a, int b, int (*op)(int, int)) {
printf("%d\n", op(a, b));
}
int main() {
calc(10, 5, add); // 传加法
calc(10, 5, subtract); // 传减法
return 0;
}
输出
15
5
解释:calc 函数通过第三个参数(函数指针)动态选择要执行的操作。把 add 或 subtract 传进去,就能在运行时完成不同的计算。
用结构体"模拟"成员函数
C 语言允许在结构体里放数据,却不能直接写函数;但可以把函数指针当作"成员",达到类似类成员函数的效果。
c
#include <stdio.h>
/* 结构体里放函数指针,当成"成员函数" */
typedef struct Rect {
int w, h;
void (*set)(struct Rect*, int, int);
int (*area)(struct Rect*);
void (*show)(struct Rect*);
} Rect;
/* 计算面积 */
int area(Rect* r) {
return r->w * r->h;
}
/* 打印尺寸 */
void show(Rect* r) {
printf("Rectangle's Width: %d, Height: %d\n", r->w, r->h);
}
/* 设置宽高 */
void set(Rect* r, int w, int h) {
r->w = w;
r->h = h;
}
/* "构造函数":把函数指针装进去 */
void constructRect(Rect* r) {
r->w = 0;
r->h = 0;
r->set = set;
r->area = area;
r->show = show;
}
int main() {
Rect r;
constructRect(&r); // 初始化
r.set(&r, 10, 5); // 调用"成员函数"
r.show(&r);
printf("Rectangle Area: %d\n", r.area(&r));
return 0;
}
yaml
输出
Rectangle's Width: 10, Height: 5
Rectangle Area: 50
函数指针数组
你还可以把函数指针放进数组里,实现一组函数的动态调用。
c
#include <stdio.h>
/* 四个运算函数 */
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int divd(int a, int b) { return b ? a / b : -1; }
int main() {
/* 声明并初始化函数指针数组 */
int (*farr[])(int, int) = {add, sub, mul, divd};
int x = 10, y = 5;
/* 通过数组下标动态调用不同函数 */
printf("Sum: %d\n", farr[0](x, y));
printf("Difference: %d\n", farr[1](x, y));
printf("Product: %d\n", farr[2](x, y));
printf("Divide: %d\n", farr[3](x, y));
return 0;
}
makefile
输出
Sum: 15
Difference: 5
Product: 50
Divide: 2