众说纷纭的 C 语言回调函数到底是什么鬼?这里有最好的解答!

Posted 嵌入式ARM

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了众说纷纭的 C 语言回调函数到底是什么鬼?这里有最好的解答!相关的知识,希望对你有一定的参考价值。


1. 什么是回调函数?


下面先说说我的看法。我们可以先在字面上先做个分解,对于"回调函数",中文其实可以理解为这么两种意思:1) 被回调的函数;2) 回头执行调用动作的函数。那这个回头调用又是什么鬼?


先来看看来自维基百科的对回调(Callback)的解析:In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execution may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback. 也就是说,把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。如果代码立即被执行就称为同步回调,如果在之后晚点的某个时间再执行,则称之为异步回调。关于同步和异步,这里不作讨论,请查阅相关资料。


再来看看来自Stack Overflow某位大神简洁明了的表述:A "callback" is any function that is called by another function which takes the first function as a parameter。也就是说,函数 F1 调用函数 F2 的时候,函数 F1 通过参数给 函数 F2 传递了另外一个函数 F3 的指针,在函数 F2 执行的过程中,函数F2 调用了函数 F3,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数 F3 就是回调函数。到此应该明白回调函数的定义了吧?


2. 为什么要使用回调函数?


很多朋友可能会想,为什么不像普通函数调用那样,在回调的地方直接写函数的名字呢?这样不也可以吗?为什么非得用回调函数呢?有这个想法很好,因为在网上看到解析回调函数的很多例子,其实完全可以用普通函数调用来实现的。要回答这个问题,我们先来了解一下回到函数的好处和作用,那就是解耦,对,就是这么简单的答案,就是因为这个特点,普通函数代替不了回调函数。所以,在我眼里,这才是回调函数最大的特点。来看看维基百科上面我觉得画得很好的一张图片。



 #include<stdio.h> #include<softwareLib.h> // 包含Library Function所在读得Software library库的头文件
int Callback() // Callback Function{ // TODO return 0; } int main() // Main program{ // TODO Library(Callback); // TODO return 0; }


乍一看,回调似乎只是函数间的调用,和普通函数调用没啥区别,但仔细一看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且丝毫不需要修改库函数的实现,这就是解耦。再仔细看看,主函数和回调函数是在同一层的,而库函数在另外一层,想一想,如果库函数对我们不可见,我们修改不了库函数的实现,也就是说不能通过修改库函数让库函数调用普通函数那样实现,那我们就只能通过传入不同的回调函数了,这也就是在日常工作中常见的情况。现在再把main()、Library()和Callback()函数套回前面 F1、F2和F3函数里面,是不是就更明白了?

明白了回调函数的特点,是不是也可以大概知道它应该在什么情况下使用了?没错,你可以在很多地方使用回调函数来代替普通的函数调用,但是在我看来,如果需要降低耦合度的时候,更应该使用回调函数。


3. 怎么使用回调函数?


知道了什么是回调函数,了解了回调函数的特点,那么应该怎么使用回调函数?下面来看一段简单的可以执行的同步回调函数代码。


 #include<stdio.h>
int Callback_1() // Callback Function 1{ printf("Hello, this is Callback_1 \n"); return 0; }
int Callback_2() // Callback Function 2{ printf("Hello, this is Callback_2 \n"); return 0; }
int Callback_3() // Callback Function 3{ printf("Hello, this is Callback_3 \n"); return 0; }
int Handle(int (*Callback)()){ printf("Entering Handle Function.\n "); Callback(); printf("Leaving Handle Function.\n "); }
int main(){ printf("Entering Main Function.\n "); Handle(Callback_1); Handle(Callback_2); Handle(Callback_3); printf("Leaving Main Function.\n"); return 0; }


运行结果:Entering Main Function.
Entering Handle Function.
Hello, this is Callback_1
Leaving Handle Function.
Entering Handle Function.
Hello, this is Callback_2
Leaving Handle Function.
Entering Handle Function.
Hello, this is Callback_3
Leaving Handle Function.
Leaving Main Function.


可以看到,Handle()函数里面的参数是一个指针,在main()函数里调用Handle()函数的时候,给它传入了函数Callback_1()/Callback_2()/Callback_3()的函数名,这时候的函数名就是对应函数的指针,也就是说,回调函数其实就是函数指针的一种用法。现在再读一遍这句话:A "callback" is any function that is called by another function which takes the first function as a parameter,是不是就更明白了呢?



4. 怎么使用带参数的回调函数?


眼尖的朋友可能发现了,前面的例子里面回调函数是没有参数的,那么我们能不能回调那些带参数的函数呢?答案是肯定的。那么怎么调用呢?我们稍微修改一下上面的例子就可以了:


 #include<stdio.h>
int Callback_1(int x) // Callback Function 1{ printf("Hello, this is Callback_1: x = %d ", x); return 0; }
int Callback_2(int x) // Callback Function 2{ printf("Hello, this is Callback_2: x = %d ", x); return 0; }
int Callback_3(int x) // Callback Function 3{ printf("Hello, this is Callback_3: x = %d ", x); return 0; }
int Handle(int y, int (*Callback)(int)){ printf("Entering Handle Function. "); Callback(y); printf("Leaving Handle Function. "); }
int main(){ int a = 2; int b = 4; int c = 6; printf("Entering Main Function. "); Handle(a, Callback_1); Handle(b, Callback_2); Handle(c, Callback_3); printf("Leaving Main Function. "); return 0; }


运行结果:

Entering Main Function.Entering Handle Function.Hello, this is Callback_1: x = 2Leaving Handle Function.Entering Handle Function.Hello, this is Callback_2: x = 4Leaving Handle Function.Entering Handle Function.Hello, this is Callback_3: x = 6Leaving Handle Function.Leaving Main Function.


可以看到,并不是直接把int Handle(int (*Callback)()) 改成 int Handle(int (*Callback)(int)) 就可以的,而是通过另外增加一个参数来保存回调函数的参数值,像这里 int Handle(int y, int (*Callback)(int)) 的参数 y。同理,可以使用多个参数的回调函数。


5.参考练习


#include <stdio.h>typedef void (*listen)(int);
listen mlisten[3];
void register_observer(listen obs){ for(int i=0;i<3;i++) { if(mlisten[i] == 0) { mlisten[i] = obs; return ; } }}
void listen0(int i){ printf("listen0 received i=%d\n",i);}void listen1(int i){ printf("listen1 received i=%d\n",i);}void listen2(int i){ printf("listen2 received i=%d\n",i);}
void notify_all_observer(int val){ for(int i=0;i<sizeof(mlisten)/sizeof(mlisten[0]);i++) { if(mlisten[i] != 0) { mlisten[i](val); } }}int main(){ int i=0; printf("lis1:%d\n",listen0); register_observer(listen0); printf("lis2:%d\n",listen1); register_observer(listen1); register_observer(listen2); while(1) { scanf("%d\n", &i); printf("%d\n",i); notify_all_observer(i); }}


-END-




推荐阅读



【01】
【02】
【03】
【04】
【05】


免责声明:整理文章为传播相关技术,版权归原作者所有,如有侵权,请联系删除

以上是关于众说纷纭的 C 语言回调函数到底是什么鬼?这里有最好的解答!的主要内容,如果未能解决你的问题,请参考以下文章

C#中的委托到底是啥概念??

PHP回调函数到底是个什么

Java虚拟机到底是什么鬼,小白看这里!

号称取代 Java 的 Kotlin 语言到底是什么鬼?

动态规划到底是什么鬼?dp数组到底是什么?

回调函数到底是怎么一回事?