回调函数
当我们的代码量比较大的时候,需要思考一个问题,是否需要合并重复代码.
这里用链表举例.比如:我们有两个需求 1.求和 2.求最大值
对于链表来说,这两个函数均会遍历链表,那么我们可以把遍历链表的方法提取出来,因为这个方法很常用,我还可以用来排序的时候用,所以如果我有很多函数用到遍历的时候,我的代码量就直线下降了.
接着,我们介绍回调函数.
1.函数指针
2.回调函数的上下文
3.通用性
首先,我们需要知道什么是函数指针,在C语言中函数名其实就是一个符号,用来表示函数的入口地址. 既然是地址我们便懂了些什么.(可以看我的博文函数指针)
接着,我们从回调函数这个名字想,回调....其实就是我在一个函数里调用另外一个函数,但是我们平时写的函数都是写死的,没有任何通用性.
仔细想想,我们可以把一个函数当成形参用么?这样就不是写死的了..这个想法非常好,我们的回调函数就是靠这个办法实现的,我们这里用到了函数指针作为一个形参.
接着好像用文字很难让人明白如何实现这个复杂的描述,我们看代码,顺便把通用性给解决了.
1 (这里就用伪代码实现了) 2 typdef struct _LinkNode 3 { 4 LinkNode *next; 5 void * data; //这里用一个void* 类型来存放数据地址,使用者会知道实际的类型,强制转化即可.通用性就增强了 6 }LinkNode; 7 8 typedef void (*CallBack)(void *data, void *ctx); 9 //定义函数指针类型, 两个形参,一个为了返回该函数返回的信息 10 11 //遍历函数的改造 12 void foreach(Link *link, CallBack c, void *ctx) 13 { 14 LinkNode *node = link->head->next; 15 while(node) 16 { 17 c(node->data,ctx); //调用回调函数,ctx看出用处了么?我们不想用全局变量,所以! 18 node = node->next; 19 } 20 } 21 22 //add方法 ,回调函数!!, 23 void add(int *data, int *ctx) 24 { 25 *ctx += *data; //这个方法是具体的整形求值方法 26 } 27 28 //最终调用该方法,放到想用的地方吧: 29 int count =0;//用来记录求和 30 forseach(link, add,&count); //很简单吧 调用
这里我们能看到30行
forseach(link, add,&count);
这个调用的后两个形参,一个是函数指针,一个是指针(变量的地址)
这样我们的目的也达到了.
总结一下使用方法:
我们用一个通用的方法(遍历),来调用特定的某个需求方法(求和).
我们将需求方法写成一个回调方法.并改造遍历方法使其通用,并可以保存需求方法的上下文.
这里最终调用的方法: 我们用函数指针作为形参定位回调函数 我们用一个变量记录回调函数的上下文