01consortium上一篇文章《枚举和结构体的结合》提到结构体就像一个包,将一些具有共同特性的变量封装在里面。结构是一种构造类型或复杂类型,可以包含多个不同类型的成员。在C语言中,还有一种与结构体非常相似的语法,叫做联合体(Union)(有些地方也叫联合体)。一个联合的例子如下:uniondata{charn;炭黑;字符f;};合并数据a、b、c;影响;虽然union的所有成员都占用相同的内存,但修改一个成员将影响所有其他成员。结构体占用内存大于等于所有成员占用内存之和(这里有一个字节对齐问题,这里不深入讨论)。union占用的内存等于最长成员占用的内存。如果在联合中为新成员赋值,则原成员的值将被覆盖。//上面的关联定义了char数据;a.n=0x0A;//虽然只修改了成员变量n,但是ch和f变量都会被改变data=a.f;//那么data的值会被修改为0x0A02应用,多见于单片机编程。在上一篇《枚举和结构体的结合》结合枚举与结构体的文章中,下面的例子简单说明了结构体关联在正文中的应用(我日常开发中一般都是用联合体配合结构体)。显示屏的应用涉及到一个概念。显示器(全色)上的一个像素由三种颜色组成:红色、绿色和蓝色。在888模式下,每个像素由8位组成。这时候要想成为一个像素点,就需要用到一个结构体,也对应上一篇文章。结构体就像包装一样,将一些具有共同特征的变量封装在里面。typedefstruct{uint8_t红色;uint8_t绿色;uint8_t蓝色;uint32_tPix_Value;}LCD_Pixvalue_S;上面的文字非常清晰易懂。您可以访问整个像素或像素的某种颜色。有一个问题。即我在操作红色像素时需要重新赋值Pix_Value成员,如下:LCD_Pixvalue_SLCD_Pixvalue;LCD_Pixvalue.Red=0x12;LCD_Pixvalue.Pix_Value=LCD_Pixvalue.Red<<16|LCD_Pixvalue.Red<<8|LCD_Pixvalue.Blue;而且内存占用也很大。当然直接使用下面的方法不会占用更多的内存,但是访问起来不方便。typedefstruct{uint32_tPix_Value;}LCD_Pixvalue_S;那么这个时候使用union和structure的结合,不仅可以占用更多的内存,还可以方便访问。typedefunion{struct{uint8_t红色;uint8_t绿色;uint8_t蓝色;}像素;uint32_tPix_Value;}LCD_Pixvalue_S;然后就可以操作LCD_Pixvalue_SLCD_Pixvalue;uint32_t数据;单独修改红色数据=LCD_Pixvalue.Pix_Value;//数据的值为0x0025FF00当然你也可以把结构体的定义写在外面,在其他地方使用,如下typedefstruct{uint8_tRed;uint8_t绿色;uint8_t蓝色;}Pix_s;typedefunion{Pix_sPix;uint32_tPix_Value;}LCD_Pixvalue_S;然后,关于内存使用,上面的定义方法定义了一个LCD_Pixvalue_S类型的变量,占用4个字节。实例图如下03union在串口开发中的应用以上是union在LCD应用中的应用实例。这是因为LCD的一个像素是由红、绿、蓝三种颜色组成的,所以使用union非常方便。在单片机项目开发中,串口协议解析也可以用到联合体中,非常方便。在私人定制协议中,合理定义协议和使用联合代码非常方便。下面的例子不谈帧头和帧尾。它们是关联在协议分析和灵活应用中的应用实例。串口协议示例如下functionbytelengthcommandlength4commandword1commandcontent7crc162那么代码可以这样写typedefunion{struct{uint32_tcmdlen;uint8_t命令;uint8_tcmdbuf[7];uint16_tcrc16;}单元;uint8_tbuffer[14];}uart_buffer_s;uart_buffer_suart_buffer;intmain(void){uint8_tlen;长度=0;uart_buffer.buffer[len++]=0x12;uart_buffer.buffer[len++]=0x34;uart_buffer.buffer[len++]=0x56;[len++]=0x78;uart_buffer.buffer[len++]=0xAA;for(inti=0;i<7;i++){uart_buffer.buffer[len++]=i;}uart_buffer.buffer[len++]=0x11;uart_buffer。缓冲区[len++]=0x22;while(1);}运行结果可以看到如下,我们只是往uart_buffer.buffer中填充数据,模拟串口接收数据,接收后会自动解析出我们自定义的cmdlen,cmd,cmdbuf和crc16。这里需要注意的是,16位和32位数据类型都是小端模式。关于little-endian模式,可以参考之前的文章?。这个还是很方便的。!!!但!!!需要注意字节对齐的问题。比如上面的cmdbuf修改为8字节,就会出现问题。下面的crc16会有问题。这就是字节对齐的问题。不懂的同学自己google一下,这里就不重点解释了。除了上述自定义协议分析中用到的union外,还可以解决浮点数float的读取问题。浮点数占用4个字节。如果把串口接收到的4个字节转成float呢?结合体可以解决这个问题。下面的示例代码显示浮点数231.5的十六进制表示为0x43678000。typedefunion{浮动数据;uint8_tbuffer[4];}uart_buffer_s;uart_buffer_suart_buffer;intmain(void){uint8_tlen;长度=0;uart_buffer.buffer[len++]=0x00;uart_buffer.buffer[len++]=0x80;.buffer[len++]=0x67;uart_buffer.buffer[len++]=0x43;while(1);}结果如下,可以看到我们模拟从串口接收4个字节,使用union,我们不需要额外写代码,就可以自动转为float类型。当然,这种转换也是小端模式。关于little-endian模式的详细介绍请参考文章《C语言在STM32中的内存分配》。
