源地址:https://argcv.com/articles/2669.cgit
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),并且方便有效,何乐而不为呢?github
callback具体怎么实现的呢?我在本文中作一个小小的demo。
Demo的实现的功能是这样的:
bar.c调用foo.c的一个函数,请求给一个magic number。但foo.c的函数并非直接返回,而是调用bar.c传递过来的函数use_magic_num,把magic number 做为参数使用进去。sql
首先,定义最后要被调用的函数use_magic_num以下:windows
1
2
3
4
5
6
|
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的原型函数
1
|
void
get_magic_num(
void
(*callback)(
int
magic));
|
它的参数列表和普通的有点区别,传入的是一个函数指针,这个函数必须是原型为有一个int类型为参数,返回为void。其余的就和普通的函数同样了。spa
对get_magic_num进行实现。线程
1
2
3
4
5
6
7
|
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的主程序code
1
2
3
4
5
|
int
main(
int
argc,
char
*argv[])
{
get_magic_num(use_magic_num);
return
0;
}
|
也就是调用foo并传入use_magic_num的函数了。
编译和执行结果大体是以下的样子:
1
2
3
4
5
6
7
|
[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]$
|
完整的工程请参考此处