在 C 语言编程中,函数指针是一个强大且核心的概念。它指的是一个指向函数的指针变量。通过这个指针,你可以间接地调用它所指向的函数。这项技术是 C 语言实现类似 C++ 中多态(Polymorphism)功能的基础,尤其在驱动开发和操作系统内核(如 Linux 内核)中应用广泛。
当我们谈论函数时,它经过编译后会变成 CPU 可以执行的机器指令,这些指令存储在内存的特定区域(通常是只读的代码段)。函数名在编译后,本质上就代表了这个函数代码块在内存中的起始地址,一个固定的常量。
理解了这一点,函数指针的概念就清晰了:我们定义一个指针变量,让它存储某个函数的入口地址。之后,通过操作这个指针,就能达到调用对应函数的目的。这为程序设计带来了极大的灵活性,例如实现回调函数和插件架构。
每个函数都有其特定的类型。例如,对于函数 int fx(char c, double d) {return 0;},它的类型是 int(char, double)。相应地,指向这类函数的指针类型就是 int(*)(char, double)。
函数指针的定义语法
那么,如何声明一个函数指针呢?其标准语法如下:
返回类型 (*指针名)(形参类型1, 形参类型2, ...) = 初始值;
关键说明:
- 指针名:必须是一个合法的标识符。
- 返回类型:若函数无返回值,此处应写
void。
- 形参列表:
(形参类型1, 形参类型2, ...) 指明了指向函数的参数类型。如果函数没有参数,可以写作 () 或 (void)。
- 初始值:定义时可以省略
= 初始值 部分。如果赋值,初始值必须是同类型的函数地址或 NULL。
如何获取函数地址并为指针赋值?
在 C 语言中,函数名本身作为一个表达式使用时,其值就是该函数的起始地址。有趣的是,对函数名使用取地址运算符 &,得到的结果也是同一个地址。两者完全等价。
例如,对于函数 void fy(double a, double b){}:
- 表达式
fy 的值是函数起始地址,类型为 void(*)(double, double)。
- 表达式
&fy 的值同样是函数起始地址,类型也是 void(*)(double, double)。
因此,为函数指针赋值时,直接用函数名或对其取地址都是正确的。
通过函数指针调用函数
得到函数指针后,调用其指向的函数有两种等价语法:
函数指针(实际调用参数1, 实际调用参数2, ...)
// 或
(*函数指针)(实际调用参数1, 实际调用参数2, ...)
第一种方式是直接通过指针调用,第二种是先解引用指针再调用。从编译器的角度来看,这两种形式产生的效果完全相同。函数调用本身也是一个表达式,拥有返回值和类型。
完整代码示例
下面我们通过一个具体例子来演示:定义一个函数指针,让它先后指向两个不同的函数,并进行调用。
// filename: function_pointer.c
#include <stdio.h>
int myadd(int x, int y) {
printf("%d + %d = %d\n", x, y, x+y);
return x + y;
}
int mymul(int x,int y) {
printf("%d * %d = %d\n", x, y, x*y);
return x * y;
}
int main(int argc, char *argv[]) {
int value;
// 声明一个指向 int(int, int) 类型函数的函数指针 pfun,并初始化为 NULL
int(*pfun)(int,int) = NULL;
pfun = myadd; // 让 pfun 指向 myadd 函数
pfun(1, 2); // 使用指针直接调用 myadd 函数
(*pfun)(3, 4); // 使用指针解引用后调用 myadd 函数
pfun = mymul;
pfun(5, 6);
(*pfun)(7, 8);
pfun = &myadd; // 函数取地址也同样是函数的地址。
value = pfun(9,10); // 将函数调用的返回值赋值给 value
printf("pfun(9, 10) 的返回值是 %d\n", value);
return 0;
}
将上述代码保存并编译运行,可以看到预期的结果:
weimingze@mzstudio:~$ gcc -o function_pointer function_pointer.c
weimingze@mzstudio:~$ ./function_pointer
1 + 2 = 3
3 + 4 = 7
5 * 6 = 30
7 * 8 = 56
9 + 10 = 19
pfun(9, 10) 的返回值是 19
进阶思考:函数指针数组
掌握了单个函数指针后,可以挑战一个更实用的结构:函数指针数组。它允许你将多个相同签名的函数组织在一起,通过索引来动态选择并调用,这在实现状态机或命令表时非常有用。
定义函数指针数组的语法如下:
返回类型 (*数组名[元素个数])(形参类型1, 形参类型2, ...) = {初始值1, 初始值2, ...};
动手实验:
尝试定义一个包含三个元素的函数指针数组,让每个元素指向一个参数和返回值类型相同的不同函数。然后编写一个循环,遍历这个数组,并依次调用每个函数指针所指向的函数。这个练习能帮助你深刻理解如何批量管理并动态调用函数,是迈向高级 C 语言编程的重要一步。
希望这篇关于 C 语言函数指针的解析能帮助你打下坚实的基础。如果你想深入探讨更多关于 C/C++ 的底层细节或系统设计模式,欢迎在云栈社区与我们交流。