C语言学习及应用笔记之七:C语言中的回调函数及使用方式

我们在使用C语言实现相对复杂的软件开发时,经常会碰到使用回调函数的问题。但是回调函数的理解和使用却不是一件简单的事,在本篇我们根据我们个人的理解和应用经验对回调函数做简要的分析。

1、什么是回调函数

既然谈到了回调函数,首先我们就要搞清楚什么是回调函数。在讨论回调函数之前,我们需要说明另一个概念,那就是函数指针。什么是函数指针呢?说的浅显一点,函数指针就是指向函数的指针,说白了也是一种指针,只是它指向的不是整型,字符型等数据量,而是指向函数。在C中,每个函数在编译后都是存储在内存中,并且每个函数都有一个入口地址,根据这个地址,我们便可以访问并使用这个函数。函数指针就是指向这个入口地址,从而调用这个函数。

同样回调函数就是一个通过函数指针调用的函数。如果我们把函数的指针(指向函数入口地址)作为参数传递给另一个函数,而接收这个参数的函数在其运行过程中,反过来使用这个指针调用其所指向的函数,我们就把这个被通过函数指针调用的函数称之为回调函数。

从上述描述我们可以知道,回调函数有别于一般意义上的函数调用方式。它一般不是由该函数的实现方直接调用,而是由已经存在的其它对象间接调用它。而且回调函数的调用是调用方所需要的,但是其具体实现却是非常灵活的,我们可以根据需要来实现它,只要调用的格式相符,我们不需要去考虑调用他的对象的具体内容。

2、为何使用回调函数

前面我们简单介绍了回调函数,那我们为什么需要使用回调函数呢?既然是用它,当然是有使用的理由。接下来我们简单的讨论一下使用回调函数的优势所在。

首先,可以使上层的应用更完整,但又不需要考虑底层的实现细节。比如我们设计了一个通讯应用,但在设计时我并不能确定底层接口,或者说不想局限于某一接口。那么我们可以将接口部分的实现留在具体使用中,所以采用回调函数的方式就非常方便。

其次,可以使应用更加灵活,这是显而易见的。比如我们设计一个通讯协议栈,这个协议栈在什么平台使用并不局限,我们使用回调的方式具体实现平台相关部分,而协议栈的内核这可以使用于多种平台。

再者,可以把调用者与被调用者分开,这样调用者不关心谁是被调用者,也不关心他的具体实现。使得软件的设计更加独立,方便与协作或者移植。其实细说起来还有很多,在此仅列举上述几点。

3、如何使用回调函数

我们已经简单的介绍了什么事回调函数以及为什么要使用它,接下来我们说说怎么使用它。对于使用方式千差万别,而且每个使用者都有相应的心得,在这里我们之宗解一下我们平时常用的几种方式。

3.1、以函数参数的形式使用

在大多数情况下,我们可能都是将函数指针作为参数传递给调用者来实现回调。比如我们声明如下函数:

void function1(int var1,int var2)

void function2(void *fc(int,int),float a,int b)

调用时咋使用function2(function1,a,b)就可以了。当然还有另一个函数与function1的声明形式一致,也一样可以做为参数传递给function2函数。

这种方式最好理解,而且函数名不受限制,只要声明形式一致就可以了。我们在外设驱动的调用上会使用这一形式。

3.2、以弱化定义的方式使用

所谓弱化函数就是调用者以_weak定义一个没有操作或者默认操作的函数,该函数允许定义与其名称和形式完全一样的函数。若使用者重新定义了该函数则会调用新函数,否则使用_weak修饰的默认函数。在STM32的HAL库中使用了很多这样的函数,比如各种msp函数。

首先需要有一个以_weak修饰的函数声明:

__weak void SetSingleCoil(uint16_t coilAddress,bool coilValue)

而在使用时定义一个与其同名且形式一样的函数:

void SetSingleCoil(uint16_t coilAddress,bool coilValue),具体个功能有使用者更具需要设定。如上述这个函数就是我们在调用Modbus协议栈时实现的,每次都不一样,根据需求而定。

这种方式使用虽然方便,但有一个局限就是必须与原函数声明一致,且只能有一个。

3.3、以函数注册的方式使用

有时候我们会对一些对象进行封装,同是将操作函数的函数指针也封装在内,这样我们可以在使用对象是直接调用其操作。这以方式组要应用于对一些复杂的外设对象的操作。如:网卡对象等,在WIZnet以及LwIP等协议栈中都是以这种方式将网卡密切相关的特定操作以函数指针的方式封装于对象中。

当然我们在开发一些外设的驱动时也可以使用这种方式。如我们开发一个外设驱动,该设备即可使用I2C接口也可使用SPI接口,我们要多次使用该设备,但每次,每个人使用那种接口是不确定的,而我们又想复用这部分驱动,但不是每次都改它,就将其作为一个对象封装起来。

定义一个结构类型,包括包括对象的主要属性和基本操作接口:

/*定义BMP280操作对象*/
typedef struct {uint8_t chipID;       //芯片IDstruct Bmp280_Calib_Param caliPara;   //校准参数struct Bmp280_Config config;  //配置寄存器struct Bmp280_Ctrl_Meas ctrlMeas;     //测量控制寄存器void (*Read)(uint8_t regAddress,uint8_t *rData,uint16_t rSize);       //读数据操作指针void (*Write)(uint8_t regAddress,uint8_t command);    //谢数据操作指针void (*Delay)(volatile uint32_t nTime);       //延时操作指针
}BMP280Device;

在使用时,我们只需声明某一特定对象,并注册相应的函数就可以使用,调用者并不关心具体接口实现。

3.4、以函数指针类型的方式使用

以声明函数指针类型的方式其实是与函数参数很类式的,也可用于形参声明,而且更简洁。但它最主要的优势在于我们可以使用其处理多个回调函数条件调用的问题。

据比如我们在处理Modbus协议时我们在处理不同功能吗的消息时,需要采用不同的处理方式,就可以采用这种方式:

定义一个枚举,同时定义一个函数指针数组:

void (*HandleSlaveRespond[])(uint8_t *,uint16_t,uint16_t)={HandleReadCoilStatusRespond,HandleReadInputStatusRespond,HandleReadHoldingRegisterRespond,HandleReadInputRegisterRespond};

这要我们通过功能码的枚举来调用不同的回调函数就非常简洁了:

HandleSlaveRespond[fuctionCode](recievedMessage,startAddress,quantity);

当然,我们只是讨论一种方法,因为使用switch语句一样可以达到效果,但是其代码量却是相差很远。

4、总结

此篇我们介绍了回调函数及其使用方式,但我们所掌握的不过冰山之一角。并且具体怎么使用它是一个见仁见智的论题,用好了自然是给程序增色,但若是随意使用反倒游客能会有问题。总而言之,回调函数是一种灵活而有强大的功能,但最终的效果还要看使用者。

欢迎关注:

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

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

相关文章

STM32与SHT1X温湿度传感器通讯

在这次项目开发中应用到了SHT1X温湿度传感器,该系列有SHT10、SHT11和SHT15,属于Sersirion温湿度传感器家族中的贴片封装系列。包括一个电容性聚合体测湿敏感元件、一个用能隙材料制成的测温元件,传感器内部有一个精度高达14为位的A/D转换器。…

STM32与MS5837压力传感器的I2C通讯

MS5837压力传感器是一种可用于电路板上,适用于检测10-1200mbar压力范围的传感器,灵敏度非常高,理论上能够检测到0.01mbar的压力变化,实际使用过程中测试并无明显的变化。 MS5837采用I2C总线通讯,与STM32的MCU可以实现…

STM32F0使用LL库实现MS5536C通讯

在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。在本文中我们说明一下,使用LL库实现MS5536C的SPI通讯。 1、MS5536C简述 MS5536C是…

STM32F0使用LL库实现DMA方式AD采集

在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。在本文中我们将介绍基于LL库的ADC的DMA采集方式。 1、概述 这次我们使用DMA方式实现对AD的采集…

STM32与宇电设备实现AI-BUS通讯

宇电的设备使用基于RS-485的自定义协议,协议本身比较简单,只有2条指令: 读:地址代号52H(82) 要读的参数代号00校验码 写:地址代号43H(67)要写的参数代号写入数低字节写…

FreeRTOS如何结束和重新启动调度程序

大多数主机或桌面系统(比如Linux,Mac或Windows)都有一个正常的用例,你可以在早上启动操作系统,然后在晚上关闭它,然后你就离开机器。嵌入式系统是不同的:他们没有参加,他们应该“永远…

先进过程控制之一:浅说APC

先进过程控制(APC)技术作为在生产装置级的信息化应用,在优化装置的控制水平和提高生产过程的管理水平的同时,还为企业创造了可观的经济效益。 1、什么是APC 先进过程控制,简称APC,并不是什么新概念。它仅…

STM32与多台MS5803压力传感器I2C通讯

MS5803压力传感器支持SPI和I2C总线通讯,拥有24位AD转换。能够同时获得压力值和温度值,其中压力测量范围为10-1100mbar,温度的测量范围是-40-85摄氏度。各引脚功能及参数如下: 传感器内部结构图如下: 通讯协议的选择通过…

STM32F0使用LL库实现SHT70通讯

在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。本篇我们将基于LL库采用模拟I2C接口的方式实现温湿度采集。 1、SHT70简述 SHT70是一款集温湿度…

STM32F0使用LL库实现PWM输出

在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。本文我们将说明如何通过LL库实现PWM信号的输出。 1、概述 我们知道STM32的TIM计时器可以输出P…

STM32F0使用LL库实现Modbus通讯

在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。本篇将说明基于LL实现USART通讯。 1、概述 我们想要实现基于RS485的Modbus通讯实际就是基于US…

STM32基于SPI和AD7192的数据采集

在开发臭氧发生器的时,我们需要一个高分辨率的AD采集,于是选择了AD7192,选择这款ADC的原因比较简单。首先它是24位的符合我们的精度要求;其次它自带时钟,便于节省空间;第三它有4路单端或2路差分输入&#x…

Modbus协议栈实现Modbus RTU多主站支持

前面我们已经详细讲解过Modbus协议栈的开发过程,并且利用协议栈封装了Modbus RTU主站和从站,Modbus TCP服务器与客户端,Modbus ASCII主站与从站应用。但在使用过程中,我们发现一些使用不便和受限的地方,所以我们就想要…

STM32基于AD5663的UV灯电压控制

在开发臭氧发生器的时,我们使用UV灯来实现臭氧的产生。而UV灯的强度决定了臭氧产生的浓度,UV灯的光强则与其控制电压密切相关。所以我们要控制产生的臭氧的浓度就需要调节其控制电压。我们选择了AD5663这一模拟量输出模块来实现这一点。 1、AD5663简介 …

实现Modbus ASCII多主站应用

前面我们已经分析了Modbus RTU的更新设计和具体实现(如果不清楚可查看前一篇文章)。其实Modbus ASCII与Modbus RTU都是基于串行链路实现的,所以有很多的共同点,基于此,这篇文章我们只讨论与Modbus RTU所不同的部分。 …

STM32一种基于NTC的控温电路及软件实现

NTC(Negative Temperature Coefficient)是一种随温度上升时,电阻值呈指数关系减小的热敏电阻。应用广泛,最近我们就采用了NTC来控制加热并测温,并达到了预期的效果。 1、硬件设计 我们使用三极管作为加热元件&#x…

STM32利用光敏二极管实现光度测量

最近我们在开发臭氧发生器时,需要监测生成的臭氧的浓度,于是想到使用光度计来测量。因为不同浓度的臭氧对管的吸收作用是不相同的,于是检测光照强度的变化就可以得到相应的浓度数据。 1、硬件设计 此次光照度检测我们选用了S1336-5BQ光电点二…

STM32的ADC通道间干扰的问题

最近我们在开发一个项目时,用到了MCU自带的ADC,在调试过程中发现通道之间村在相互干扰的问题。以前其实也用过好几次,但要求都不高所以没有太关注,此次因为物理量的量程较大,所以看到了变化。 首先来说明一下此次的软…

实现Modbus TCP多网段客户端应用

对于Modbus TCP来说与Modbus RTU和Modbus ASCII有比较大的区别,因为它是运行于以太网链路之上,是运行于TCP/IP协议之上的一种应用层协议。在协议栈的前两个版本中,Modbus TCP作为客户端时也存在一些局限性。我们将对这些不足作一定更新。 1、…

改进初学者的PID-介绍

最近看到了Brett Beauregard发表的有关PID的系列文章,感觉对于理解PID算法很有帮助,于是将系列文章翻译过来!在自我提高的过程中,也希望对同道中人有所帮助。作者Brett Beauregard的原文网址:http://brettb…