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地址。…

gn入门(Chromium)

Chromium是用gn和ninja进行编译的,即gn把.gn文件转换成.ninja文件,然后ninja根据.ninja文件将源码生成目标程序。gn和ninja的关系就与cmake和make的关系差不多。 代码下载:https://download.csdn.net/download/arv002/88668243 1. 环境配置 在我们自己的项目中,也可以使用…

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…

NumPy 中级教程——通用函数(ufuncs)

Python NumPy 中级教程&#xff1a;通用函数&#xff08;ufuncs&#xff09; NumPy 中的通用函数&#xff08;ufuncs&#xff09;是一种能够对数组进行元素级操作的函数&#xff0c;支持数组的快速、逐元素的操作&#xff0c;是进行数据处理的关键工具之一。在本篇博客中&…

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;家庭火灾发生的频率越来越高。火灾报警…

快排例题——第k个数

做道简单一点的题巩固一下 基本思路 与上一个整体相似&#xff0c;通过快速排序将长度为n的整数数列变为从小到大的有序数组&#xff0c;在拿出其中第k个数。 题目描述 给定一个长度为 n 的整数数列&#xff0c;以及一个整数 k&#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;和照片墙&#…

C++的一些知识

一. 语法 move怎么用 https://blog.csdn.net/zhangmiaoping23/article/details/126051520 这个文章讲的很好&#xff0c;其中有一些疑惑的点 (1) 左值引用不能接右值 class T1{int a; }; int main(){T1 t1 T1();T1 && t1_temp T1(); //T1()是一个临时对象&#xf…

WPF 新手指引弹窗

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

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

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

Go语言程序设计-第6章--方法

Go语言程序设计-第6章–方法 对象就是简单的一个值或者变量&#xff0c;并且拥有其方法&#xff0c;而方法是某种特定类型的函数。 6.1 方法的声明 方法的声明和普通函数的声明类似&#xff0c;只是在函数名字前面多了一个参数。这个参数把这个方法绑定到这个参数对应的类型…

【影像组学入门百问】#58---#62

#58-使用PyRadiomics能提取出多少影像组学特征&#xff1f; 当前支持以下特征类别&#xff1a; 第一阶段统计量 基于形状的&#xff08;3D&#xff09; 基于形状的&#xff08;2D&#xff09; 灰度共生矩阵&#xff08;GLCM&#xff09; 灰度共生长度矩阵&#xff08;GLRLM&a…

springboot接口文档

Swagger 在Spring Boot中生成和维护接口文档的一个常用方法是使用Swagger。Swagger是一个开源软件框架,它帮助开发者设计、构建、记录和使用RESTful Web服务。下面是在Spring Boot项目中使用Swagger来创建接口文档的详细步骤:1. 添加Swagger依赖 在你的Spring Boot项目的pom…

Go语言中的包管理工具之Go Path的使用

GoLang 中常用的包管理的方式 1 &#xff09;概述 常用的有三种 Go PathGo VendorGo Modules 2 &#xff09;发展历程 早期go的包管理存在很大缺陷&#xff0c;甚至可以说没有官方统一的包管理工具 一方面官方在努力发布一些实验性的包管理工具。同时也出现了很多社区开发…

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

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

JAVA进化史: JDK8特性及说明

JDK 8&#xff08;Java Development Kit 8&#xff09;是Java平台的一个重大版本&#xff0c;于2014年3月发布。该版本引入了许多令人期待的新特性&#xff0c;其中一些改变了Java语言的面貌&#xff0c;提供了更丰富、灵活和现代的编程体验。以下是JDK 8的一些主要特性&#x…