注册函数和回调函数使用讲解

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.可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。
在这里插入图片描述
在系统设计中,分层模型。常规的逻辑是,上层调用下层的方法。比如,一个驱动程序,对下屏蔽了硬件细节,对上提供了调用接口。但也会存在下层调用上层方法的情况,这时如果直接调用调用的话,会使得下层的软件特定于一个上层的软件,降低复用性。

使用回调的话,就相对灵活了。调用者不需要关心具体的实现细节,只需要知道存在一个具有特定原型的函数即可。‌ 这种机制使得代码更加模块化和可维护,避免了直接调用带来的灵活性问题‌。

  1. 在程序执行期间可以动态更改被调用的回调函数。

  2. 比直接调用,显得逼格高!

欢迎大家留言评论!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/893652.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ESP32移植Openharmony外设篇(1)MQ-2烟雾传感器

外设篇 实验箱介绍 旗舰版实验箱由2部分组成&#xff1a;鸿蒙外设模块&#xff08;支持同时8个工作&#xff09;、鸿蒙平板。 其中&#xff0c;鸿蒙平板默认采用RK3566方案。 OpenHarmony外设模块采用底板传感器拓展板方式&#xff0c;底板默认采用ESP32方案&#xff0c;也…

部署Qwen2.5-7b大模型详解

部署Qwen2.5-7b大模型详解 本文参考教程&#xff1a;https://qwen.readthedocs.io/en/latest/getting_started/quickstart.html 下载模型 https://modelscope.cn/organization/qwen 搜索 qwen2.5-7b 可以看到它提供了六个模型&#xff0c;以满足不同的需求&#xff0c;从下…

HBuilder X中搭建Vue-cli项目组件和路由以及UI库使用(二)

一、创建组件 &#xff08;1&#xff09;在vj1项目src|右键|vue文件 &#xff08;2&#xff09;组件常用模版 <!--该标签用于写HTML代码,必须有一个根标签,如下<div>是根标签--> <template> <div>首页</div> </template><!--该标签用…

c++算法第3天

本篇文章包含三道算法题&#xff0c;难度由浅入深&#xff0c;适合新手练习哟 目录 第一题 题目链接 题目解析 代码原理 代码编写 本题总结 第二题 题目链接 题目解析 代码原理 代码编写 第三题 题目链接 题目解析 代码原理 代码编写 第一题 题目链接 [NOIP2…

【word】页眉横线无法取消

小伙伴们日常想在页眉里加横线&#xff0c;直接双击页眉&#xff0c;然后在页眉横线里选择自己喜欢的横线样式就可以了。 但今天我遇到的这个比较奇特&#xff0c;有些页有这个横线&#xff0c;有些页没有&#xff0c;就很奇怪。 最后排查完&#xff0c;发现是只有标题2的页…

拓数派创始人冯雷出席联合国人居署《未来城市顾问展望2024》 报告结题专家会

近日&#xff0c;联合国人居署中国未来城市顾问委员会在内蒙古鄂尔多斯市国际会展中心召开《未来城市顾问展望2024&#xff1a;数字城市治理》报告结题会暨走进鄂尔多斯市活动。拓数派创始人、董事长兼首席执行官冯雷&#xff08;Ray Von&#xff09;应邀出席本次活动&#xff…

《计算机视觉》—— 疲劳检测

文章目录 一、疲劳检测实现的思想二、代码实现 一、疲劳检测实现的思想 了解以下几篇文章有助于了解疲劳检测的方法 基于dlib库的人脸检测 https://blog.csdn.net/weixin_73504499/article/details/142977202?spm1001.2014.3001.5501 基于dlib库的人脸关键点定位 https://blo…

个人博客搭建 | Hexo框架

文章目录 1.Hexo安装2.创建博客3.将博客通过GitHub来部署4.更换主题 1.Hexo安装 Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown&#xff08;或其他标记语言&#xff09;解析文章&#xff0c;在几秒内&#xff0c;即可利用靓丽的主题生成静态网页。搭建Hexo首先要…

基于vue框架的的大连金州红星社区物业管理系统dg6co(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;楼栋信息,住户,社区投诉,设备报修,报修完成,车位信息,缴费信息,房屋信息,维修工,保安,来访人员,缴费申诉,公共设备,设备类型,消防设备,公共场地 开题报告内容 基于Vue框架的大连金州红星社区物业管理系统的设计与实现开题报告 一、研究…

如果使用 Iptables 配置端口转发 ?

现实生活中&#xff0c;港口转发就像在一个大型公寓大楼里告诉送货司机该去哪里。通常情况下&#xff0c;该建筑群的正门是不对外开放的。但如果里面有人想要快递&#xff0c;他们可以告诉保安让司机进来&#xff0c;并指引他们到特定的公寓。 类似地&#xff0c;在计算机网络…

Android复杂问题分析工具bugreportz详解

文章目录 bugreportz详细介绍功能与作用使用方法生成详细报告检查进度bugreportz 的优势分析报告 如何分析1. 解压 ZIP 文件2. 分析主要文件2.1 bugreport.txt2.2 logcat.txt2.3 kernel.log / last_kmsg2.4 events.log2.5 traces.txt2.6 dumpstate_board.txt 3. 工具支持4. 重点…

Axure重要元件三——中继器添加数据

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 本节课&#xff1a;中继器添加数据 课程内容&#xff1a;添加数据项、自动添加序号、自动添加数据汇总 应用场景&#xff1a;表单数据的添加 案例展示&#xff1a; 步骤…

SpringColoud GateWay 核心组件

优质博文&#xff1a;IT-BLOG-CN 【1】Route路由&#xff1a; Gateway的基本构建模块&#xff0c;它由ID、目标URL、断言集合和过滤器集合组成。如果聚合断言结果为真&#xff0c;则匹配到该路由。 Route路由-动态路由实现原理&#xff1a; 配置变化Apollo 服务地址实例变化…

No.17 笔记 | XXE漏洞:XML外部实体注入攻击

1. XXE漏洞概览 XXE&#xff08;XML External Entity&#xff09;是一种允许攻击者干扰应用程序对XML输入处理的漏洞。 1.1 XXE漏洞比喻 想象XML解析器是一个听话的机器人&#xff0c;而XXE就是利用这个机器人的"过分听话"来获取不应该获取的信息。 1.2 XXE漏洞危…

vue综合指南(六)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Vuet篇专栏内容:vue综合指南 目录 101、Vue 框架怎么实现对象和数组的监听&#xff1f; 102、Proxy 与 Object.d…

运营商DNS vs 公共DNS,IT运维的你选对了吗?

在IT运维中&#xff0c;选择运营商DNS还是公共DNS是一个需要综合考虑多方面因素的决策。 一、运营商DNS 优点 1. 速度与可用性&#xff1a; • 运营商DNS服务器通常部署在本地或邻近地区&#xff0c;因此能够提供较低的延迟和更快的解析速度。 • 运营商通常会投入大量资源来…

Java利用itextpdf实现pdf文件生成

前言 最近公司让写一个数据页面生成pdf的功能&#xff0c;找了一些市面代码感觉都太麻烦&#xff0c;就自己综合性整合了一个便捷的工具类&#xff0c;开发只需简单组装数据直接调用即可快速生成pdf文件。望大家一起学习&#xff01;&#xff01;&#xff01; 代码获取方式&am…

java游戏网站源码

题目&#xff1a;java游戏网站源码 编号B22A390 主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Mysql|大数据|SSM|SpringBoot|Vue|Jsp|MYSQL等)、学习资料、JAVA源码、技术咨询 文末联系获取 感兴趣可以先收藏起来&#xff0c;以防走丢&#xff0c;有任何选题、文档编…

初识C++--C++入门

一、命名空间 在c语言中存在着名字冲突的问题&#xff0c;即不能出现同名&#xff0c;会出现错误。而在c中变量、函数和后⾯要学到的类都是⼤量存在的&#xff0c;这些变量、函数和类的名称将都存在于全局作⽤域中&#xff0c;可能会导致很多冲突。为了解决这个问题&#xff0c…

十三、Python基础语法(字符串str-中)

一、切片 使用下标可以获得字符串中指定的一个字符&#xff0c;使用切片可以获取字符中多个字符。 字符串[start: end: step] start&#xff1a;开始位置的下标 end&#xff1a;结束位置的下标&#xff08;end对应的位置数据取不到&#xff09; step&#xff1a;步长&#…