首页 > 其他分享 >在实际应用中联合体union的妙用

在实际应用中联合体union的妙用

时间:2022-11-14 23:31:11浏览次数:40  
标签:妙用 struct union 成员 联合体 char int

关键字union,又称为联合体、共用体,联合体的声明和结构体类似,但是它的行为方式又和结构体不同,这里的行为方式主要指的是其在内存中的体现,结构体中的成员每一个占据不同的内存空间,而联合体中的所有成员共用的是内存中相同的位置。

       简单看下区别:

struct MyStruct 
{
double a;
int b;
char c;
};
struct MyStruct

union MyUnion 
{
double a;
int b;
char c;
};
union MyUnion

        同样是定义变量value;内存空间占用情况如下:

在实际应用中联合体union的妙用_数据结构

可以看出,结构体变量中3个成员相当于3个人,每个人必须要住一间屋子,优点是空间包容性强,但是内存空间必须全部分配,不管房子住不住人。联合体变量3个成员,它们可以共用一间屋子,但是每个屋子同一时间只能容纳一个成员,因此不够包容,成员是互斥的,但是可以大大节省内存空间。

要注意的是,联合体的长度大小为最大的成员的大小,在本例中即value.a的大小。并不是单指数据类型,若在MyUnion定义了数组char c[10],则此时该联合体变量value大小为10个字节。

以上简单的了解了下union的基本定义,在实际应用中我们一般都使用结构体来定义数据组合而成的结构型变量,而在各数据类型各变量占用空间差不多并且对各变量同时使用要求不高的场合(单从内存使用上)也可以灵活的使用union。

1、变量的初始化

在初始化的时候,只应对一个成员进行初始化即在初始化列表中只有一个初始值。原因就是联合体的所有成员共用一个首地址,在默认情况下,会将这个初始值初始化给联合体变量的第一个成员。

union MyUnion 
{
double a;
int b;
char c;
};
//为第一个成员初始化
union MyUnion un1 = {5.0f};
//错误初始化,不能为多个成员初始化
union MyUnion un1 = {5.0f, 10};
//对其它位置的成员进行初始化,则可以通过指定初始化方式
union MyUnion un1 = {.b = 10};
//与结构体一样,也可以将一个联合体变量作为初始值,直接初始化给同类型的另一个联合体变量
union MyUnion

2、数据位操作

#include<stdio.h>
typedef struct
{
unsigned char bit0:1;
unsigned char bit1:1;
unsigned char bit2:1;
unsigned char bit3:1;
unsigned char bit4:1;
unsigned char bit5:1;
unsigned char bit6:1;
unsigned char bit7:1;
}bitValue;

typedef union
{
unsigned char bytedata;
bitValue bitdata;
}regValue;

int main()
{
regValue data;
data.bytedata= 0x5A;
printf("%d",data.bitdata.bit5); //读取第6位
data.bitdata.bit7 = 1; //修改第8位
return 0;
}

可以看出,通过访问和修改联合体中的定义bitdata成员,可以间接的访问和修改定义的bytedata的值,这可以用在嵌入式的寄存器位操作上。

3、和struct嵌套使用

比如我们分别定义电视和空调的属性:

struct tvFeature    //电视属性
{
char *logo; //品牌
int price; //价格
int screensize //屏幕尺寸
int resolution //分辨率
}tvFeature;
struct tvFeature tvfeature;

struct airFeature //空调属性
{
char *logo; //品牌
int price; //价格
int coldcapacity;//制冷量
int hotcapacity;//制热量
}airFeature;
struct airFeature


可以看出电视和空调有相同的属性,也有各自特有的属性。我们可以使用家用电器的数据结构统一定义。但是这样用统一的数据结构,定义电视和空调的变量之间耦合会增加很多,对于tvfeature和airfeature各自来说用不到的属性也会浪费内存空间。

struct homeappliancesFeature  //电器属性
{
char *logo; //品牌
int price; //价格
int screensize //屏幕尺寸
int resolution //分辨率
int coldcapacity;//制冷量
int hotcapacity;//制热量
}homeappliancesFeature;

struct homeappliancesFeature tvfeature;
struct homeappliancesFeature

因此可以用union来解决问题:

struct tvFeature    //电视属性
{
int screensize //屏幕尺寸
int resolution //分辨率
}tvFeature;
struct airFeature //空调属性
{
int coldcapacity;//制冷量
int hotcapacity;//制热量
}airFeature;

struct homeappliancesFeature //电器属性
{
char *logo; //品牌
long country; //国家
union
{
struct tvFeature tvST;
struct airFeature airST;
};
};
struct homeappliancesFeature tvfeature;
struct homeappliancesFeature

如上我们只需一个结构体,就可解决电视和空调的属性不同问题;struct tvFeature tvST和struct airFeature airST共用一块内存空间,定义变量时,可以访问各自的特有属性,这样就解决了内存浪费和变量耦合高的问题。

4、数据复制

        例如串口数据发送时,可以直接使用数据复制的方式将数据打包发送,不需要将一个4字节的数据额外进行拆分为4个单字节的数据;反之读取数据时,也可以不用将4个单字节的数据重新通过移位拼接为一个4字节数据。

typedef union
{
uint8 data8[4];
uint32 data32;
}dataType;

uint32 sendData = 0x5A5AA5A5;
uint32 receiveData;
dataType commSend;
void main(void)
{
uint8 commData[128];
//数据复制
commData.data32 = sendData;
//发送数据,字节复制,不需要再将commData.data32单独移位拆分
commData[0]= commSend.data8[0];
commData[1]= commSend.data8[1];
commData[2]= commSend.data8[2];
commData[3]= commSend.data8[3];

//读取数据时,字节复制,不需要再将已经读取到的4个单字节数据拼接
receiveData = commData.data32;
}

5、分时发送不同帧格式数据

        比如需要在同一段通信数据发送逻辑中,针对不同通信协议帧格式进行发送时,就可以这样定义数据结构。

typedef struct 
{
uint8 head; //帧头格式相同
union //中间数据格式不一样
{
struct //payloadType1
{
uint8 cmd;
uint8 type;
uint8 data[5];
uint8 check;
}msgType1;

struct //payloadType2
{
uint16 cmd;
uint8 data[8];
uint16 check;
}msgType2;

uint8 data[10]; //payloadType3
} payloadType;
uint8 end; //帧尾格式相同
}frameType;

        By the way:在使用联合体时可以注意这两个点:

1、数据大小端

        使用联合体时需要注意数据大小端问题,这个取决于实际的处理器的存储方式。
        大端存储就是高字节数据放在低地址。
        小端存储就是高字节数据放在高地址。
        如下方例子,可以知道使用的处理器的存储方式:

#include<stdio.h>
union Un
{
int i;
char c;
};
union Un un;

int main()
{
un.i = 0x11223344;
if (un.c == 0x11)
{
printf("大端\n");
}
else if (un.c == 0x44)
{
printf("小端\n");
}
}

2、指针方式访问

       由于在一个成员长度不同的联合体里,分配给联合体的内存大小取决于它的最大成员的大小。如果内部成员的大小相差太大,当存储长度较短的成员时,浪费的空间是相当可观的,在这种情况下,更好的方法是在联合体中存储指向不同成员的指针而不是直接存储成员本身。所有指针的长度都是相同的,这样能解决内存空间浪费的问题。

#include<stdio.h>
typedef struct
{
unsigned char a;
int b;
}stValue1;

typedef struct
{
int c;
unsigned char d[10];
double e;
}stValue2;

//联合体成员定义为指针成员
union Un
{
stValue1 *ptrSt1;
stValue2 *ptrSt2;
};

int main()
{
union Un *info;
info->ptrSt1->a = 5;
info->ptrSt2->e = 9.7f;
}

       总之在实际使用联合体union过程中一句话总结:围绕成员互斥和内存共享这两个核心点去灵活设计你的数据结构。


更多技术内容和书籍资料获取敬请关注微信公众号“明解嵌入式”

在实际应用中联合体union的妙用_联合体_02

标签:妙用,struct,union,成员,联合体,char,int
From: https://blog.51cto.com/Sharemaker/5851237

相关文章

  • CF1743F Intersection and Union 题解
    更好体验线段的贡献不好统计,考虑统计每一个点在不同情况中的被覆盖次数,那么每个点的被覆盖次数总和即为答案。设\(f_{i,j,0/1}\)表示\(i\)点在扫描到线段\(j\)时是......
  • MySQL union 和 order by 同时使用
    目录一、出现错误的情况二、解决上述问题的两种方法三、案例分析:求解:常见的错误解法(1)使用union和多个orderby不加括号【报错】(2)orderby在union子句中不起作用正......
  • 【Hive】MapJoin限制场景之一(MapJoin Followed by Union)
    举例说明Hive实现MapJoin限制场景之一,MapJoinFollowedbyUnionHive的MapJoin逻辑会有几个限制场景:UnionFollowedbyaMapJoinLateralViewFollowedbyaMapJoin......
  • [Typescript] 91. Hard - Union to Intersection
    Implementtheadvancedutiltype UnionToIntersection<U>ForexampletypeI=Union2Intersection<'foo'|42|true>//expectedtobe'foo'&42&true htt......
  • 嵌入式-C语言基础:联合体和共用体的概念
    有时候同一块内存空间存放类型不同,不同类型的变量共享一块空间。结构体和共用体的区别:(1)结构体元素有各自单独空间,共用体元素共享空间,空间大小由最大类型确定。(2)结构体元......
  • c语言借助GNU创建对象和嵌套union使用
    #include<stdio.h>#include<stdlib.h>#include<string.h>/**枚举体占用内存*枚举有符号,根据数值分配内存。[1~4]*但是和编译器相关联,目前我看到的都是4by......
  • L - Intersection and Union Gym - 103993L (线段树)
    题意思路思路很巧妙,首先是枚举每个值的贡献,然后找到了规律,下次做题的时候线分析每个题有啥好规律,然后根据规律做题。再就是线段树的这个思路,感觉很巧妙,通过设置每一段的......
  • Rust 元組匹配的一個妙用
    Rust元組匹配的一個妙用原文:https://nathanael-morris-bennett.medium.com/rust-tuple-pattern-matching-trick-c0f6bcdb4460PS:原文的示例代碼中有幾處語法錯誤,本文對......
  • C语言--共用体(联合体)union
    共用体: 多个变量(不同的数据类型)共用同一块内存空间,但是同一时刻,只能有一个变量起作用共用体中起作用的的成员是最后一次存放的成员  #include<stdio.h>#inclu......
  • Rust那些事之Vector妙用
    导语我们知道vector是一个动态数组,在C++中来说,vector中存储的只能是一种类型,那如何做到存储多个,无非就是包一层,例如:结构体、​​void*​​。而在Rust中enum非常独特,每个值可......