在C/C++中,我们经常会像下面的代码那样使用一个指向函数的指针,我们称之为函数指针:
// demo.c
#include <stdio.h>
int func(int a) {
return a + 1;
}
int main(int argc, char* argv[]) {
int (*f)(int) = func;
printf("%p\n", f);
return 0;
}
上面的例子中,我们定义了一个函数func
,然后通过函数指针f
指向func
,接着使用print
函数打印指针变量f
指向
的地址。代码平淡无奇,接着我们编译代码,然后使用objdump -D demo
来查看生成的二进制结构如下:
0000000100003f34 <_func>:
100003f34: ff 43 00 d1 sub sp, sp, #16
100003f38: e0 0f 00 b9 str w0, [sp, #12]
100003f3c: e8 0f 40 b9 ldr w8, [sp, #12]
100003f40: 00 05 00 11 add w0, w8, #1
100003f44: ff 43 00 91 add sp, sp, #16
100003f48: c0 03 5f d6 ret
上述结果是我在arm64-apple-darwin21.6.0环境下,使用clang 14.0编译出来的结果,gcc编译的结果稍微有点区别,但是对于本文分析问题影响不大。
正如我们所看到的,编译后的func
函数位于0x102e0bf34
地址,让我们记住这个地址,接着往后看。
接下来我们来运行编译后的二进制文件,很显然它应该输出的是func
函数在内存中的地址:
./demo
0x102e0bf34
你猜对了,只是尽管函数指针也是指针,它指向的是内存中的一段代码,而不是内存中的数据。
通常情况下,函数指针作为回调是没有问题的,但是如果考虑到闭包的情况,我们发现函数指针就无能为力了。 因为闭包不仅是一个函数,而且还要能捕获相关的数据,由于函数指针仅仅是指向内存中的一段代码,并没有指向内存中的数据, 所以函数指针无法实现闭包的功能。如果要想实现,就需要做一些改进:
typedef void (*func) (void *);
struct closure {
func f;
void *arg;
};
如上,我们定义了一个结构closure
,其中包含了两部分,一个指针变量指向一个函数,一个变量保存参数。
也就是说,closure
既包含了一段代码,也包含了运行该代码所需要的数据,或者称之为上下文环境,不管它叫什么,总之就是
运行函数所需要的数据。
这其实就是C++中的std::function
所实现的功能。
在C++中,你无法单纯使用函数指针来指向对象的成员函数,原因就是函数指针无法捕获其上下文(指向对象的指针)。std::function
所做的与我们上面定义的closure
结构其实没有太多本质的区别。
使用std::function
,我们不仅仅可以存储一段代码,还可以存放必要的执行上下文,然后在合适的时候基于该上下文进行函数调用。
评论