1.概念
注册和回调函数在C语言编程中非常常见,也经常用到。注册和回调的机制也大量使用在Linux内核中。学会使用注册和回调函数是C语言开发者应当掌握的一项编程技能。
函数的本质在内存上体现的是地址。我们知道函数的地址后,就能够调用这个函数。
这里面涉及到两个函数:注册函数A, 回调函数B
所谓注册的过程,就是把一个 指向某个函数B的指针作为参数传递给 注册函数A。注册函数A使用变量(一般是全局变量)把 指向函数B的指针给保存下来。
回调函数,即当因某个事件触发时,通过函数指针调用被保存的函数。
2. 示例
我们来看一个例子来理解注册和回调函数。
2. 1 无参数无返回值的回调函数
#include <stdio.h>
// 定义一个回调函数类型, 无参数无返回值
typedef void (*CallbackFunction)();
// 声明一个全局变量来存储注册的回调函数
CallbackFunction g_Callback = NULL;
// 注册回调函数的函数
void registerCallback(CallbackFunction callback) {
g_Callback = callback;
}
// 模拟的事件触发函数,它会调用注册的回调函数
void triggerEvent() {
if (g_Callback != NULL) {
g_Callback();
} else {
printf("No callback function registered!\n");
}
}
// 用户定义的回调函数1
void userCallbackFunction1() {
printf("User callback function1 called!\n");
}
// 用户定义的回调函数2
void userCallbackFunction2() {
printf("User callback function2 called!\n");
}
int main() {
// 注册用户定义的回调函数1
registerCallback(userCallbackFunction1);
// 触发事件,这将调用注册的回调函数1
triggerEvent();
// 注册用户定义的回调函数2
registerCallback(userCallbackFunction2);
// 触发事件,这将调用注册的回调函数2
triggerEvent();
// 取消注册(可选)
registerCallback(NULL);
return 0;
}
2. 2 有参数无返回值的回调函数
下面的回调函数,带有一个int参数
#include <stdio.h>
// 定义一个回调函数类型, 有参数无返回值
typedef void (*CallbackFunction)(int num);
// 声明一个全局变量来存储注册的回调函数
CallbackFunction g_Callback = NULL;
// 注册回调函数的函数
void registerCallback(CallbackFunction callback) {
g_Callback = callback;
}
// 模拟的事件触发函数,它会调用注册的回调函数
void triggerEvent() {
printf("Please enter a number.\n");
if (g_Callback != NULL) {
int num;
scanf("%d",&num);
g_Callback(num);
} else {
printf("No callback function registered!\n");
}
}
// 用户定义的回调函数1
void userCallbackFunction1(int num) {
printf("User callback function1 called!\n");
printf("num= %d!\n",num);
}
// 用户定义的回调函数2
void userCallbackFunction2(int num) {
printf("User callback function2 called!\n");
printf("num= %d\n",num);
}
int main() {
// 注册用户定义的回调函数1
registerCallback(userCallbackFunction1);
// 触发事件,这将调用注册的回调函数1
triggerEvent();
// 注册用户定义的回调函数2
registerCallback(userCallbackFunction2);
// 触发事件,这将调用注册的回调函数2
triggerEvent();
// 取消注册(可选)
registerCallback(NULL);
return 0;
}
2. 3 有参数有返回值的回调函数
返回值int, 参数int
#include <stdio.h>
// 定义一个回调函数类型, 有参数有返回值
typedef int (*CallbackFunction)(int num);
// 声明一个全局变量来存储注册的回调函数
CallbackFunction g_Callback = NULL;
// 注册回调函数的函数
void registerCallback(CallbackFunction callback) {
g_Callback = callback;
}
// 模拟的事件触发函数,它会调用注册的回调函数
void triggerEvent() {
printf("Please enter a number.\n");
if (g_Callback != NULL) {
int num;
scanf("%d",&num);
int result = g_Callback(num);
printf("result= %d!\n",result);
} else {
printf("No callback function registered!\n");
}
}
// 用户定义的回调函数1
int userCallbackFunction1(int num) {
printf("User callback function1 called!\n");
return num;
}
// 用户定义的回调函数2
int userCallbackFunction2(int num) {
printf("User callback function2 called!\n");
return num;
}
int main() {
// 注册用户定义的回调函数1
registerCallback(userCallbackFunction1);
// 触发事件,这将调用注册的回调函数1
triggerEvent();
// 注册用户定义的回调函数2
registerCallback(userCallbackFunction2);
// 触发事件,这将调用注册的回调函数2
triggerEvent();
// 取消注册(可选)
registerCallback(NULL);
return 0;
}
相信,通过上面的例子,大家已经掌握了注册和回调函数的使用了吧
3.注册和回调的好处
下面我们探讨下,使用注册和回调函数的好处。
1.可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。
在系统设计中,分层模型。常规的逻辑是,上层调用下层的方法。比如,一个驱动程序,对下屏蔽了硬件细节,对上提供了调用接口。但也会存在下层调用上层方法的情况,这时如果直接调用调用的话,会使得下层的软件特定于一个上层的软件,降低复用性。
使用回调的话,就相对灵活了。调用者不需要关心具体的实现细节,只需要知道存在一个具有特定原型的函数即可。 这种机制使得代码更加模块化和可维护,避免了直接调用带来的灵活性问题。
-
在程序执行期间可以动态更改被调用的回调函数。
-
比直接调用,显得逼格高!
欢迎大家留言评论!