回调函数与其机制:
是什么?
一句话概括:A调B,并把C当参数传给B,B运行,再调用了充当参数的C。
如下图:
解析:应用层的函数A调底层的函数B,顺便把应用层的函数C当参数传给B,而B在底层执行的时候,需要回到应用层调用C,这个过程就叫回调,而函数C就是回调函数。
为什么?
就拿最常用的理由来说,假设甲和乙分别做镜头移动控制和镜头自动对焦,两个人,分开写代码,这个时候,乙需要用到镜头的位置信息,聚焦情况等,而这些东西是由甲所能够直接获得的,乙也不知道,那么,怎么办?
可能有人会说:“过去问甲啊,让它把代码发给你,然后你再复制过去,加几个库文件什么的,直接在你那里获取了这些信息就行了啊!”
可能有人人会说:“你让甲把获取信息的接口给你,你执行的时候再调用那个接口,回到甲那里拿数据即可。”
换做是你,你会怎么办?
很明显,当然第二种方法更好,看下面分析:
①代码复杂程度:第一种方法,需要拿到甲的代码,然后才能获取到信息,这无疑是增加了代码量;而后者,只需拿甲的接口,即可获得信息;
②模块的耦合性:第一种方法,需要在乙的模块中加入了甲的模块,这样一来,模块间的耦合性就大了,如果是在获取信息的时候出错,到底是甲本身写错了,还是说乙在写这一部分的时候粗心大意漏写了什么?这样一来,双方的维护就麻烦了;而后者,乙仅仅拿了甲的接口,模块间的耦合性大大减少,出现了问题之后,一查,就能够知道是甲的问题还是乙的问题了。
所以,为了让代码更加简洁,让模块间的耦合性更加的低,不同模块之间使用回调往往是一个比较好的办法。
怎么做?
回调,这个过程,需要用到几个比较重要的知识点:
①函数指针:
指针我们比较熟悉了,例如常见的int *p,p变量就是一个指针,指向的是int类型的数据,同理可得,int (*p_func) (char),这是函数指针,变量p_func是一个指针变量,它指向的是一个返回值为int,参数为char的函数,注意,括号必须使用,否则就是指针函数了(一个返回int类型的指针的函数)。
一般来说,使用typedef定义回调函数类型,后面,就可以直接使用这个类型了,如:
typedef int (*p_func_cb_t) (char);
②注册回调函数:
乙需要用到甲提供的接口,那么,甲的接口函数给到乙的这个过程就叫注册回调函数。注册的方式主要为两种,一种是独立出注册函数,另一种是直接在参数中接收,如下:
独立注册方式:
p_func_cb_t g_func_cb;
void register_callback(p_func_cb_t get_func_cb) //这个接口留给甲,甲把乙用到的函数传进来
{
g_func_cb = get_func_cb; //此时,全局变量已接向甲处了,待会可以直接调用
}
void test()
{
//omit
g_func_cb(); //此时,调用了在甲的函数,完成回调
}
在参数中注册:(此方法仅适用于同一个文件下)
test(p_func_cb_t get_func_cb)
{
//omit
get_func_cb(); //此时,也已经执行了回调函数
}
现在看下来,是不是有点头绪了呢?
来,再看看完整的代码:
(甲)
#include <stdio.h>
#include "st_enc.h"
st_fun_cb g_myfun_cb;
int test_myfun(int x,int y)
{
printf("I am in test_myfun,x+y = %d !\n",x+y);
return 0;
}
int test_my_minus_fun(int x,int y)
{
printf("_%s,%d | x - y = %d \n",__FILE__, __LINE__, x-y);
return 0;
}
int g_x,g_y;
int main()
{
/*a call back function register method which just assign the function pointer with another function name*/
g_myfun_cb = test_myfun;
g_x =0;
g_y = 0;
printf("this is going to test 'call back fucntion and its sync or async'...\n");
/*assume that st_call is a function without open source,but i have to change something...*/
/*for example,I'prefer to use minus than add */
/*the only thing i need to change is the g_myfun_cb, I can change its orientation*/
st_call(g_myfun_cb,g_x,g_y);
/*if you register a callback funtion to a global variable in a lib , you can use it when you prefer*/
printf("test another situation:...\n");
g_myfun_cb(10,20);
st_call(g_myfun_cb,0,0);
/*register cb func*/
register_call_back_func(test_my_minus_fun);
calculate_cb(10,8);
return 0;
}
另一个文件(乙):
```c
#include <stdio.h>
#include "st_enc.h"
#include <unistd.h>
int register_call_back_func(st_fun_cb from_up_cb) {
g_acp_reg_cb = from_up_cb;
return 0; }
int calculate_cb(int x, int y) {
st_fun_cb inner_fun_cal_cb = g_acp_reg_cb;
if(x>0 && y>0)
{
printf("__%s,%d | I am ready to call inner_fun_cal_cb... \n",__FILE__,__LINE__,y-x);
inner_fun_cal_cb(x,y);
}
return 0;
}
int st_call(st_fun_cb myfun, int x, int y) {
printf("I am in st_call function!\n");
sleep(1);
/*this period, cb funtion may be called by others...*/
printf("I am going to call the cb function by myself...\n");
myfun(x,y);
return 0; }
头文件:
#ifndef __ST_ENC_H__
#define __ST_ENC_H__
typedef int (*st_fun_cb) (int ,int);
int register_call_back_func(st_fun_cb from_up_cb);
int calculate_cb(int x, int y);
st_fun_cb g_acp_reg_cb;
int st_call(st_fun_cb myfun,int x,int y);
#endif
makefile:
all:test_callback
Target = test_callback
Source = st_enc.c call_back_test.c
GCC = gcc
Include = -I./
Lib = -lpthread
$(Target):$(Source)
$(GCC) $(Source) $(Include) $(Lib) -o $(Target)
clean:
rm -r $(Target)
实验结果: