本帖最后由 jcky001 于 2024-2-6 16:10 编辑
C/C++中可以使用指针指向一段代码,这个指针就叫函数指针,假设有这样一段代码:
#include <stdio.h>
int func(int a) {
return a + 1;
}
void main() {
int (*f)(int) = func;
printf("%p\n", f);
}
我们定义了一个函数func,然后使用指针变量f指向该函数,然后打印出变量f指向的地址,代码很简单,然后我们编译一下,看下编译后生成的指令,我们重点关注func函数:
0000000000400526 <func>:
400526: 55 push %rbp
400527: 48 89 e5 mov %rsp,%rbp
40052a: 89 7d fc mov %edi,-0x4(%rbp)
40052d: 8b 45 fc mov -0x4(%rbp),%eax
400530: 83 c0 01 add $0x1,%eax
400533: 5d pop %rbp
400534: c3 retq
可以看到,编译好后的函数func位于地址0x400526这个地址,让我们记住这个地址。
然后运行一下编译后生成的程序,想一想这段代码会输出什么呢?
显然应该是func函数的在内存中的地址!
$ ./a.out
0x400526
没有猜错吧,实际上函数指针本质也是一个指针,只不过这个指针指向的不是内存中的一段数据而是内存中的一段代码,就像这样:
看到了吧,我们常说的指针一般都是指向内存中的一段数据,而函数指针指向了内存中的一段代码,在这个示例中指向了内存地址0x400526,在这个地址中保存了函数func的机器指令。
现在你应该明白函数指针了,细心的同学可能会有一个疑问,为什么编译器在生成可执行文件时就知道函数func存放在内存地址0x400526上呢?这不应该是程序被加载到内存后开始运行时才能确定的吗?
函数指针的作用是可以把一段代码当做一个变量传来传去,主要的用途之一就是回调函数。关于回调函数,可以参考《回调函数的实现原理》这篇文章。
关于回调函数其实是在A模块定义,在B模块被调用,就像这样:
然而有时我们会有这样的场景,我们依然需要在模块A定义函数,同时函数A的运行需要依赖B模块产生的数据,然后将模块A定义的函数和模块B产生的数据一并传递给C模块来调用,就像这样:
|