callback,函数的回调,从ANSI C开始,一直被广为使用。无论是windows API的所谓消息机制,动态链接库的调用,还是sqlite的命令,gcc下的pthread,qsort。callback都在其中起着难以替代的作用。
那么,callback为何如此受到亲睐呢?因为它可以以一个函数为参数,放到参数列表交给API,让API调用。
比如有个文件夹下有一千万个文件,我们希望对每个文件进行一些处理,比如查看文件长度是否超过512字节。而遍历这个文件夹的程序是个API。如果不使用callback,API可能有两种办法。
一种是申请一个超巨大的空间,然后把找到的所有文件名都放进去,然后返回这个空间(类似的还有把这组文件名写到文件中,一个意思),这样速度的确很快,而且简单方便,但问题是,一千万个文件,占用的空间是如此的庞大,空间复杂度为O(n),而我们只是要对每个文件进行一点小小的处理而已,实在是杀鸡焉用宰牛刀。
另一个方法是搞个迭代器,每次只返回一个文件名,下次调用再返回下个。为了线程安全,每次传参都得来个指针之类的记录自己的进度。如果做的细心,也可以做的很快,也不占用空间。但问题是,只是个简单的遍历文件夹的API,这么折腾好累哦。而如果使用callback,事情就简单很多了。主程序调用API,把处理文件的函数用参数的方法传递给API,API每找到一个文件名,就调用这个函数,当场处理不用存储任何文件名。这样空间复杂度是O(1),而且方便有效,何乐而不为呢?
callback具体怎么实现的呢?我在本文中做一个小小的demo。
Demo的实现的功能是这样的:
bar.c调用foo.c的一个函数,请求给一个magic number。但foo.c的函数并不是直接返回,而是调用bar.c传递过来的函数use_magic_num,把magic number 作为参数使用进去。
首先,定义最后要被调用的函数use_magic_num如下:
void use_magic_num(int magic_num); void use_magic_num(int magic_num) { printf("bar.c : magic number is %d \n", magic_num); }
也就是传进去一个参数,然后函数会打印出这个magic number。它被放置在bar.c下
然后foo.h中定义API get_magic_num的原型
void get_magic_num(void (*callback)(int magic));
它的参数列表和普通的有点区别,传入的是一个函数指针,这个函数必须是原型为有一个int类型为参数,返回为void。其他的就和普通的函数一样了。
对get_magic_num进行实现。
typedef void (*foo_run_magic)(int); void get_magic_num(foo_run_magic foo) { int magic = 9; foo(magic); }
我么可以看到,在这儿,我做了点小花招,我typedef了这个函数指针,这样我们可以直接使用foo_run_magic来代替麻烦的各种括号的函数指针了。
然后调用 foo,其实就是调用传入的参数。在本工程中,也就是前面写的use_magic_num了。
虽然foo.c完全看不见use_magic_num,但完全不妨碍函数的使用。
最后定义bar.c的主程序
int main(int argc,char *argv[]) { get_magic_num(use_magic_num); return 0; }
也就是调用foo并传入use_magic_num的函数了。
编译和执行结果大致是如下的样子:
[yu@argcv callback]$ make gcc -c -Wall -std=c99 bar.c -o bar.o gcc -c -Wall -std=c99 foo.c -o foo.o gcc -Wall -std=c99 -o bar bar.o foo.o [yu@argcv callback]$ ./bar bar.c : magic number is 9 [yu@argcv callback]$
完整的工程请参考此处
19 Comments
枫叶红秋雨 · January 14, 2014 at 12:17
和php同样
yu · January 14, 2014 at 15:29
@枫叶红秋雨 我之所以好喜欢php,用php做胶水语言管理我的很多系统配置,就是因为它和我所爱的c有好多奇妙的相似
eliteYang · January 6, 2014 at 18:23
最近也在研究这些,在写通用网络库,里面无数的callback啊,看的都头晕了
yu · January 6, 2014 at 19:41
@eliteYang 习惯了这个设定,其实也是很带感的。
自己给自己写一些工具类的代码,常常可以用这个。爽爆了
百家争鸣 · January 6, 2014 at 12:58
厄 好好学习一下
Era · January 6, 2014 at 11:56
你学的啥,感觉看不懂哈。
yu · January 6, 2014 at 15:53
@Era 计算机,C语言啊
kitten0 · January 2, 2014 at 14:11
不明觉厉,好羡慕程序员
够靓博客 · January 1, 2014 at 23:19
新年快乐。
youran · January 1, 2014 at 21:14
新年快乐~
yu · January 1, 2014 at 21:21
@youran 多谢
youran · January 1, 2014 at 21:13
Happy New Year!
yu · January 1, 2014 at 21:37
额 .. akismet 这货误杀好厉害的样子
小草元 · January 1, 2014 at 15:36
高大上了。还是祝新年快乐吧。
馒头饭MADfan · January 1, 2014 at 14:54
不明觉厉,新年快乐!
爱浮夸 · January 1, 2014 at 13:23
新年好。
yu · January 1, 2014 at 13:40
多谢,话说你的网站不支持评论的说
wapeter · December 23, 2013 at 13:27
共享一个C的测试架构, 也使用了function pointer可以当玩具
yu · December 23, 2013 at 14:50
@wapeter 多谢。
之前写一个机器学习方面的工作,抽取feature。
有很多重复的预处理工作,我也是这么处理的。好方便呢