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

完整的工程请参考此处

Categories: Code

Yu

Ideals are like the stars: we never reach them, but like the mariners of the sea, we chart our course by them.

19 Comments

wapeter · December 23, 2013 at 13:27

Safari 6.0.5 Safari 6.0.5 Mac OS X  10.8.5 Mac OS X 10.8.5

共享一个C的测试架构, 也使用了function pointer可以当玩具

// gcc -std=c99 -o testcase testcase.c
// by peter _ @ _ wapeter.com
#include <stdio.h>
#include <stdlib.h>

typedef int (*testcase_t) (int, char*[]); //  testcase_t;

int test0(int argc, char *argv[]) {
    printf("test0 argc=%dn", argc);
    return 0;
}

int test1(int argc, char *argv[]) {
    printf("test1 argc=%d (bug)n", argc);
    return -2;
}

// test 2 [db] [name] [pass]
int test_db(int argc, char *argv[]) {
    const char *db = "mydb";
    const char *name = "peter";
    const char *pass = "12345";

    if (argc > 2) { db = argv[2]; }
    if (argc > 3) { name = argv[3]; }
    if (argc > 4) { pass = argv[4]; }
    // test mysql_real_connect  (removed)
    printf("test_db: db=%s  name=%s  pass=%sn", db, name, pass);

    return 0;
}

const testcase_t test_list[] = {
        test0
,       test1
,       test_db // better use testN where N is the index
};


int test_selector(int argc, char *argv[])
{
    int testmax = sizeof(test_list) / sizeof(test_list[0]);
    int testcase = testmax - 1 ; // TESTCASE_MAX-1;
    int ret;

    if (argc > 1) {
        testcase = atoi(argv[1]);
    }

    printf("RUN test%d:n", testcase);
    if (testcase >= testmax) {
        printf("ERR invalid testcase %dn", testcase);
        return -2;
    }
    ret = test_list[testcase](argc, argv);

    printf("RET %dn", ret);
    if (ret < 0) {
        printf("XXXXXXXXX BUG test %d ret < 0: %dn"
        , testcase, ret);
    }

    return ret;
}

int main(int argc, char *argv[])
{
    int ret;
    ret = test_selector(argc, argv);
    return ret;
}

    yu · December 23, 2013 at 14:50

    Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 GNU/Linux x64 GNU/Linux x64

    @wapeter 多谢。
    之前写一个机器学习方面的工作,抽取feature。
    有很多重复的预处理工作,我也是这么处理的。好方便呢

爱浮夸 · January 1, 2014 at 13:23

Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 Windows 7 Windows 7

新年好。

    yu · January 1, 2014 at 13:40

    Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 Windows 8.1 x64 Edition Windows 8.1 x64 Edition

    多谢,话说你的网站不支持评论的说

馒头饭MADfan · January 1, 2014 at 14:54

Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 Windows XP Windows XP

不明觉厉,新年快乐!

小草元 · January 1, 2014 at 15:36

Safari 4.0.5 Safari 4.0.5 iPhone iOS 4.0 iPhone iOS 4.0

高大上了。还是祝新年快乐吧。

youran · January 1, 2014 at 21:13

Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 Windows 7 x64 Edition Windows 7 x64 Edition

Happy New Year!

    yu · January 1, 2014 at 21:37

    Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 GNU/Linux x64 GNU/Linux x64

    额 .. akismet 这货误杀好厉害的样子

youran · January 1, 2014 at 21:14

Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 Windows 7 x64 Edition Windows 7 x64 Edition

新年快乐~

    yu · January 1, 2014 at 21:21

    Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 GNU/Linux x64 GNU/Linux x64

    @youran 多谢

够靓博客 · January 1, 2014 at 23:19

Google Chrome 21.0.1180.89 Google Chrome 21.0.1180.89 Windows 7 Windows 7

新年快乐。

kitten0 · January 2, 2014 at 14:11

Internet Explorer 9.0 Internet Explorer 9.0 Windows 7 x64 Edition Windows 7 x64 Edition

不明觉厉,好羡慕程序员

Era · January 6, 2014 at 11:56

Maxthon 4.0 Maxthon 4.0 Windows XP Windows XP

你学的啥,感觉看不懂哈。

    yu · January 6, 2014 at 15:53

    Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 GNU/Linux x64 GNU/Linux x64

    @Era 计算机,C语言啊

百家争鸣 · January 6, 2014 at 12:58

TheWorld Browser TheWorld Browser Windows 7 Windows 7

厄 好好学习一下

eliteYang · January 6, 2014 at 18:23

Google Chrome 31.0.1650.57 Google Chrome 31.0.1650.57 Windows 7 x64 Edition Windows 7 x64 Edition

最近也在研究这些,在写通用网络库,里面无数的callback啊,看的都头晕了

    yu · January 6, 2014 at 19:41

    Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 Windows 7 x64 Edition Windows 7 x64 Edition

    @eliteYang 习惯了这个设定,其实也是很带感的。

    自己给自己写一些工具类的代码,常常可以用这个。爽爆了

枫叶红秋雨 · January 14, 2014 at 12:17

Google Chrome 31.0.1650.63 Google Chrome 31.0.1650.63 Windows 8.1 x64 Edition Windows 8.1 x64 Edition

和php同样

    yu · January 14, 2014 at 15:29

    Google Chrome 32.0.1700.72 Google Chrome 32.0.1700.72 Windows 8.1 x64 Edition Windows 8.1 x64 Edition

    @枫叶红秋雨 我之所以好喜欢php,用php做胶水语言管理我的很多系统配置,就是因为它和我所爱的c有好多奇妙的相似

Leave a Reply to eliteYang Cancel reply

Your email address will not be published. Required fields are marked *