回调函数与其回调机制

tech2022-08-10  132

回调函数与其机制:

是什么?

一句话概括: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)

实验结果:

最新回复(0)