C/C++面向对象(OOP)编程-回调函数详解(回调函数、异步编程、函数指针、)

本文主要介绍回调函数的使用,包括函数指针、异步回调编程、主要通过详细的例子来指导在异步编程和事件编程中如何使用回调函数来实现。

🎬个人简介:一个全栈工程师的升级之路!
📋个人专栏:C/C++精进之路
🎀CSDN主页 发狂的小花
🌄人生秘诀:学习的本质就是极致重复!

目录

1 回调函数

2 C语言回调函数

2.1 函数指针

2.1.1 函数指针变量

2.1.2 定义函数类型

2.1.3 定义函数指针类型

2.2 C语言回调函数实例

2.3 C语言异步编程


1 回调函数

        回调函数是一种特殊的函数,它作为参数传递给另一个函数,并在被调用函数执行完毕后被调用。这种机制常用于事件处理、异步编程和处理各种操作系统和框架的API。

具体来说,一个函数可以将其它的函数(也就是回调函数)作为参数传入,当特定的事件触发或者任务完成时,这个被传入的函数就自动执行。这样的设计使得代码更加模块化,增强了代码的可读性和可重用性。

        回调函数是一种设计理念,与编程语言无关,C语言中可以通过函数指针实现回调函数

        回调函数主要用于异步编程和事件处理中。

        回调函数提供了一种强大的工具,允许我们将复杂的问题分解为更小的部分进行解决,同时也增加了代码的灵活性和扩展性。

2 C语言回调函数

        C语言的回调函数是使用函数指针实现的,函数作为参数传给另一个函数,传参的函数被称为回调函数。

2.1 函数指针

2.1.1 函数指针变量

        指向函数的指针变量称为函数指针。本质上是一个指针。

        定义方式:

        返回类型 (*指针变量名)(参数列表);

        其中,返回类型表示函数返回值的类型,参数列表表示函数接受的参数类型和个数。指针变量名就是函数指针的名称,它可以像普通变量一样被赋值和使用。

        使用实例:

  例如,定义一个指向返回值为int类型、接受两个int类型参数的函数的指针:

int (*pFunc)(int, int);

C++中可以使用new运算符动态分配内存来存储函数指针:

int (*pFunc)(int, int) = new int(*func)(int a, int b);

其中,func是一个已定义的函数,pFunc是指向该函数的指针。使用delete运算符释放内存:

delete pFunc;

2.1.2 定义函数类型

       定义方式:

        typedef 返回类型 新的函数名(参数列表);

        使用实例:

定义一个返回类型是int,参数为两个int数据,的函数类型

typedef int INT_func(int,int);

        调用方式:

int sub(int a, int b)
{return (a-b);
}// add 为指针类型,函数的地址,因此需要声明变量为指针类型
INT_func *add_p = add;
int ret = add_p(12,13);

一个例子:

#include <stdio.h>typedef int INT_func(int,int);int add(int a,int b)
{return (a+b);
}int sub(int a, int b)
{return (a-b);
}int multi(int a,int b)
{return (a*b);
}int main()
{INT_func *add_p = add;int ret = add_p(12,13);printf("12+13 = %d \n",ret);INT_func *sub_p = sub;int ret1 = sub_p(12,13);printf("12-13 = %d \n",ret1);INT_func *mutti_p = multi;int ret2 = mutti_p(12,23);printf("12*13 = %d \n",ret2);return 0;
}

        运行结果:

2.1.3 定义函数指针类型

         定义方式:

        typedef 返回类型 (*新的函数名)(参数列表);

        使用实例:

定义一个返回类型是int,参数为两个int数据,的函数类型

typedef int (*INT_func)(int,int);

        调用方式:

int sub(int a, int b)
{return (a-b);
}// add 为指针类型,函数的地址,因此需要声明变量为指针类型
INT_func  add_p = add;
int ret = add_p(12,13);

一个例子:

#include <stdio.h>typedef int (*INT_func)(int,int);int add(int a,int b)
{return (a+b);
}int sub(int a, int b)
{return (a-b);
}int multi(int a,int b)
{return (a*b);
}int main()
{INT_func add_p = add;int ret = add_p(12,13);printf("12+13 = %d \n",ret);INT_func sub_p = sub;int ret1 = sub_p(12,13);printf("12-13 = %d \n",ret1);INT_func mutti_p = multi;int ret2 = mutti_p(12,23);printf("12*13 = %d \n",ret2);return 0;
}

        运行结果:

2.2 C语言回调函数实例

        在C语言中,回调函数是一种常见的编程模式,它允许一个函数作为参数传递给另一个函数。这种特性使得我们可以将一些特定的任务委托给其他函数来完成。

#include <stdio.h>// 定义一个函数指针类型
typedef void (*Callback)(int);// 定义一个函数,接受一个回调函数作为参数
void do_something(Callback callback) {// 在这里做一些操作...printf("Doing something...\n");// 然后调用回调函数callback(42);
}// 定义一个回调函数
void my_callback(int data) {printf("Callback called with data: %d\n", data);
}int main() {// 将回调函数传递给do_something函数do_something(my_callback);return 0;
}

        在这个例子中,do_something函数接受一个Callback类型的参数,这是一个指向函数的指针。然后,do_something函数调用这个回调函数,并将一个整数作为参数传递给它。

my_callback函数是一个简单的回调函数,它接受一个整数参数并打印出来。

main函数中,我们将my_callback函数传递给do_something函数,这样当do_something函数完成其操作后,它会调用my_callback函数。

        运行结果:

2.3 C语言异步编程

        回调函数在异步编程中有着重要的作用,在接口编程应用广泛,实战中经常会注册一个回调函数来接收其他程序返回的数据,这样做的好处是调用者无需主动去获取被调用者的数据,仅仅需要回调,让被调用者去传送数据,这样就形成了回调,这是回调最常见的应用。

        假设A是视频流数据,每隔一段时间会将数据传送给B,B有两种接收方式:

        (1)A将视频数据存储好,提供接口,B根据自己的需求去调用,无需考虑时间,主动权在B。

        (2)A利用回调机制,实现回调函数,当数据来临时通知B来取数据,B注册回调函数接收数据,这样主动权在A。

        很显然第一种取数据的方式,由于B不知道数据何时来临比较低效,而第二种取数据方式A通知B来取数据,效率比较高,间接的实现了中断取数。

        这里就用到了回调,B注册一个函数,将函数地址传给A,A中调用该地址获取到B注册的函数时,我们就称这个函数为回调函数。

一个例子:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>// A的实现,一般会隐藏
typedef void (*CallbackPtr)(int); //函数指针定义typedef struct parameter{int a ;CallbackPtr callback;
}parameter; // 创建实例
parameter ParaData;void* callback_thread(void *p1)//此处用的是一个线程
{//do something// 循环改变p->a的值为为0 2 3 4 5 6 7 8 9 ,每隔3秒改变一次parameter* p = (parameter*)p1 ;while(1){sleep(3);//延时3秒执行callback函数p->callback(p->a);//函数指针执行函数,这个函数来自于应用层Bp->a = (p->a + 1) % 10;}
}void startup_app_A()
{// 初始化为0ParaData.a = 0;CallbackPtr init_call_back;ParaData.callback = init_call_back;//创建线程pthread_t thing1;pthread_create(&thing1,NULL,callback_thread,(void *) &ParaData);
}// 给B的接口,接收注册函数
extern void SetCallBackFun(CallbackPtr callback)
{printf("SetCallBackFun print! \n");ParaData.callback = callback;
}// //-----------------------应用者B-------------------------------
void fCallBack(int a)       // 应用者增加的函数,此函数会在A中被执行
{//do somethingprintf("B得到A的数据 = %d\n",a);
}int main(void)
{// 启动Astartup_app_A();SetCallBackFun(fCallBack);// 主函数while (1){// std::cout << "main function" << std::endl;printf("main function\n");sleep(2);}return 0;
}

        运行结果:

        分析结果,可以看出A提供的数据会回调B,B会每隔一定的时间收到数据,完成回调,对于一般的编程会将A的操作封装起来,只提供以下接口和函数指针类型:

typedef void (*CallbackPtr)(int);
extern void SetCallBackFun(CallbackPtr callback);

        这样B可以每次注册回调函数获取A传来的数据,如下:

void fCallBack(int a)       // 应用者增加的函数,此函数会在A中被执行
{//do somethingprintf("B得到A的数据 = %d\n",a);
}

        主函数中调用:

SetCallBackFun(fCallBack);

        这样就完成了一个C语言回调的异步编程,这在驱动领域极其常见,比如摄像头输出50fps的UVC流,程序员用UVC来完成相应的任务,这样就可以使用异步回调的方式实现。

🌈我的分享也就到此结束啦🌈
如果我的分享也能对你有帮助,那就太好了!
若有不足,还请大家多多指正,我们一起学习交流!
📢未来的富豪们:点赞👍→收藏⭐→关注🔍,如果能评论下就太惊喜了!
感谢大家的观看和支持!最后,☺祝愿大家每天有钱赚!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黑马程序员SSM框架-SpringBoot

视频连接&#xff1a;SpringBoot-01-SpringBoot工程入门案例开发步骤_哔哩哔哩_bilibili SpringBoot简介 入门程序 也可以基于官网创建项目。 SpringBoot项目快速启动 下面的插件将项目运行所需的依赖jar包全部加入到了最终运行的jar包中&#xff0c;并将入口程序指定。 Spri…

【网络面试(2)】DNS原理-域名和IP地址的查询转换

从上一篇博客我们得知浏览器是如何生成了HTTP消息了&#xff0c;但是浏览器作为应用程序&#xff0c;是不具备向网络中发送请求的能力&#xff0c;而是需要委托给操作系统的内核协议栈来发送请求。在委托协议栈之前&#xff0c;浏览器还要做的一件事情就是将域名转换为IP地址。…

java进阶(三)

IO与网络编程 3 输入输出流IO3.1 基础定义3.2 IO框架3.3 读取字节输入流-InputStream3.3.1 InputStream.read3.3.2 FileInputStream类说明 3.4读取字符输入流Reader3.4.1 Reader.read3.4.2 FileReader类说明 3.5 字节输出流OutputStream3.5.1 OutputStream.write3.5.2 FileOutp…

nodejs微信小程序+python+PHP特困救助供养信息管理系统-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

分库分表之Mycat应用学习四

4 分片策略详解 分片的目标是将大量数据和访问请求均匀分布在多个节点上&#xff0c;通过这种方式提升数 据服务的存储和负载能力。 4.1 Mycat 分片策略详解 总体上分为连续分片和离散分片&#xff0c;还有一种是连续分片和离散分片的结合&#xff0c;例如先 范围后取模。 …

基于AT89C51单片机可做实物的温度烟雾火灾报警设计

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/88658141?spm1001.2014.3001.5503 C 源码仿真图毕业设计实物制作步骤02 摘要 随着现代家庭用火、用电量的增加&#xff0c;家庭火灾发生的频率越来越高。火灾报警…

浅学lombok

Lombok&#xff08;Project Lombok&#xff09;是一个用于 Java 编程语言的开源库&#xff0c;旨在减少 Java 代码中的冗余和样板代码&#xff0c;提高开发人员的生产力。它通过使用注解来自动生成 Java 类的常见方法和代码&#xff0c;从而使开发人员能够编写更简洁、更具可读…

ColibriSM v1.4.2 已注册– 仿脸书类社交网络PHP源码

ColibriSM&#xff1a;当代社交网络平台的崛起 一、开篇与概述 在信息时代的浪潮中&#xff0c;社交媒体成为了人们日常生活中不可或缺的一部分。ColibriSM&#xff0c;作为一个仿照脸书&#xff08;Facebook&#xff09;、推特&#xff08;Twitter&#xff09;和照片墙&#…

WPF 新手指引弹窗

新手指引弹窗介绍 我们在第一次使用某个软件时&#xff0c;通常会有一个“新手指引”教学引导。WPF实现“新手指引”非常方便&#xff0c;且非常有趣。接下来我们就开始制作一个简单的”新手指引”(代码简单易懂&#xff0c;便于移植)&#xff0c;引用到我们的项目中又可添加一…

Windows电脑引导损坏?按照这个教程能修复

前言 Windows系统的引导一般情况下是不会坏的&#xff0c;小伙伴们可以不用担心。发布这个帖子是因为要给接下来的文章做点铺垫。 关注小白很久的小伙伴应该都知道&#xff0c;小白的文章都讲得比较细。而且文章与文章之间的关联度其实还是蛮高的。在文章中&#xff0c;你会遇…

使用uni-app editor富文本组件设置富文本内容及解决@Ready先于onload执行,无法获取后端接口数据的问题

开始使用富文本组件editor时&#xff0c;不知如何调用相关API设置富文本内容和获取内容&#xff0c;本文将举例详解 目录 一.了解editor组件的常用属性及相关API 1.属性常用说明 2.富文本相关API说明 1&#xff09;editorContext 2&#xff09; editorContext.setContents…

【Leetcode】第 378 场周赛

文章目录 100166. 检查按位或是否存在尾随零题目题意代码 100185. 找出出现至少三次的最长特殊子字符串 I题目思路代码 100184. 找出出现至少三次的最长特殊子字符串 II 100166. 检查按位或是否存在尾随零 题目 题意 这里题目要求的是或运算&#xff0c;所以原数组中只需要有…

LeetCode每日一题.04(不同路径)

一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff1f; 示例 1…

Spring Cloud + Vue前后端分离-第10章 基于阿里云OSS的文件上传

源代码在GitHub - 629y/course: Spring Cloud Vue前后端分离-在线课程 Spring Cloud Vue前后端分离-第10章 基于阿里云OSS的文件上传 前面介绍的文件上传是基于本地文件服务器的文件上传&#xff0c;但是自己搭文件服务器会有很多运维的问题&#xff0c;比如磁盘满了要扩容…

Spark SQL简介与基本用法

Apache Spark是一个强大的分布式计算框架&#xff0c;Spark SQL是其组件之一&#xff0c;用于处理结构化数据。Spark SQL可以使用SQL查询语言来查询和分析数据&#xff0c;同时还提供了与Spark核心API的无缝集成。本文将深入探讨Spark SQL的基本概念和用法&#xff0c;包括数据…

Flink Job 执行流程

Flink On Yarn 模式 ​ 基于Yarn层面的架构类似 Spark on Yarn模式&#xff0c;都是由Client提交App到RM上面去运行&#xff0c;然后 RM分配第一个container去运行AM&#xff0c;然后由AM去负责资源的监督和管理。需要说明的是&#xff0c;Flink的Yarn模式更加类似Spark on Ya…

mysql间隙锁demo分析

概述 通常用的mysql都是innodb引擎&#xff1b; 一般在update的时候用id都会认为是给行记录加锁&#xff1b; 在使用非唯一索引更新时&#xff0c;会遇到临键锁&#xff08;范围锁&#xff09;&#xff1b; 临键锁和表中的数据有关&#xff1b; mysq版本:8 隔离级别&#xf…

jmeter的常用功能及在测试中的基本使用和压测实战

Jmeter基础功能 了解Jmeter的常用组件 元件&#xff1a;多个类似功能组件的容器&#xff08;类似于类&#xff09; 一&#xff1a;Test Plan&#xff08;测试计划&#xff09; 测试计划通常用来给测试的项目重命名&#xff0c;使用多线程脚本运行时还可以配置线程组运行方式…

【C++】STL 容器 - map 关联容器 ① ( std::map 容器简介 | std::map 容器排序规则 | std::map 容器底层实现 )

文章目录 一、std::map 容器1、std::map 容器简介2、std::map 容器排序规则3、std::map 容器底层实现 二、代码示例 - std::map 容器1、代码示例2、执行结果 一、std::map 容器 1、std::map 容器简介 std::map 容器 是 C 语言 标准模板库 ( STL , Standard Template Library ) …

分布式技术之数据复制技术

文章目录 什么是数据复制技术&#xff1f;数据复制技术原理及应用同步复制技术原理及应用异步复制技术原理及应用半同步复制技术原理及应用三种数据复制技术对比 什么是数据复制技术&#xff1f; 数据复制是一种实现数据备份的技术。数据复制技术&#xff0c;可以保证存储在不…