callback 用法简介

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]$

完整的工程请参考此处