[ 什么是回调函数 ]传送门: https://www.zhihu.com/question/19801131
[ 大写的PS ]: 如果对理论不感兴趣或者觉得冗长,请直接跳到后面 [ 上菜 ]部分 !
:D
在许多形形色色的技术博客上,经常会看到许多类似下面这样的程序段的用法,简直灭绝人性。
[ 灭绝人性的程序写法示例 ]
int call(char *p, int (*ptr)())
{
//do something
}
[ 为何要用typedef ] typedef本身具有很强大的功能,这里不细说。正是在上面这种个人认为不好的编程形式下,建议用 typedef 进行稍微的处理转换,既清晰又美观,何乐不为呢?当然,这是本文进行所谓简明化处理的一个基础。
下面来说说回调函数的几个要点:
[ 被调用的函数A ] 可以认为是最后核心的被执行的函数;
[ 对被调用函数进行调用的函数B ] 可以认为是绑定这个被调函数A的地方,让它以一种什么样的形式来使用(例如在循环体中执行N次,对别的变量赋值等等);
[ 触发主动调用函数的函数C ] 真正开始触发各个函数被调用的地方。
在上面的这三个函数A、B、C中,函数A和C通常是在一个应用层级来写的,而函数B则可能在较低的层级实现。
例如:
在进行嵌入式的开发过程中:
芯片源厂给出的SDK中,可能根据芯片使用者的需要提供了一些底层的库函数,这些函数有许多往往需要在应用逻辑之中的一些状态变量来选择具体如何执行,这些库函数就是函数B,也就是被调函数被其直接调用的这一些函数;
而在基于源厂SDK进行开发的过程中,芯片使用者所实现的一些应用逻辑的功能函数,就是函数A或者函数C,具体看是哪一个被传入函数B的,哪一个是主动调B的,被当做参数传入给B的,就是回调函数A,主动调B的,是真正触发各个函数被调用的函数C。
再具体细化一个例子:
场景:为使用某款带WIFI功能的模块进行开发工作,这个模块的状态有两种:AP和STA方式。
应用层写了一个函数 is_router_connect() 用来判断路由器是否处于连接状态;这个函数中,需要使用底层的一个由芯片源厂实现的函数 get_status_of_moudel() 来获取当前模块的工作状态;
而这个get_status_of_moudel() 需要传入一个应用层是否有向发射队列写入数据任务的参数waiting_work_queues,来表明是对模块的AP状态还是STA状态下的各项模块状态信息进行获取,并且当底层的状态发生改变时,需要底层自动上报一个状态改变的信息给到is_router_connect() 或其他应用层的函数,以进行相应的处理。
参数waiting_work_queues是从另一个上层应用函数 get_work_queues_num() 来获取的,它提供给了这个底层函数一个程序走向(即进行何种处理)的依据;
这时,is_router_connect()就是函数C,get_status_of_moudel()就是函数B,get_work_queues_num()就是函数A,值得注意的是,C调B 和 B调A的调用中还有一些其他逻辑的处理。
[ 上菜! ]
程序中,上述函数A B C分别有了比较“见之识义”的名字。
#include
#include
//简单点,让我们命名的方式简单点
/* 定义一种输出为int型,输入参数为两个int型的这一类的函数,并给它一个统一的类别名字,callBackfunName*/
typedef int(*funName)(int x1, int x2);
/* 下述 add() 和 mix() 是两个符合funName这种形式的函数,通常它们处在应用层的下一层,即库函数或者外部接口等*/
int add(int x, int y)
{
printf("add() is called.\n");
return (x+y);
}
int mix(int x, int y)
{
printf("mix() is called.\n");
return (x*y);
}
void call_by_funName(funName f,int x)//函数B,调用调用函数A的函数 callBackfunName此时就被当做int char等一样的形式来用!
{
int ret = -100;
printf("call_by_funName is begain.\n");
ret = f(x,x);
printf("call_by_funName is finished.\n");
printf("[ %d ]\n",ret);
}
int main()//函数C,调用B的函数,首先引起各个调用的地方
{
callBackfunName f1 = mix;
callBackfunName f2 = add;
call_by_funName(f1,10);
call_by_funName(f2,20);
system("pause");
return 0;
}
[ 结果截图 ]