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

简单看下区别:

1 struct MyStruct
2 {
3 double a;
4 int b;
5 char c;
6 };
7 struct MyStruct value;
1 union MyUnion
2 {
3 double a;
4 int b;
5 char c;
6 };
7 union MyUnion value;

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

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

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

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

  • 1、变量的初始化

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

 1 union MyUnion
2 {
3 double a;
4 int b;
5 char c;
6 };
7 //为第一个成员初始化
8 union MyUnion un1 = {5.0f};
9 //错误初始化,不能为多个成员初始化
10 union MyUnion un1 = {5.0f, 10};
11 //对其它位置的成员进行初始化,则可以通过指定初始化方式
12 union MyUnion un1 = {.b = 10};
13 //与结构体一样,也可以将一个联合体变量作为初始值,直接初始化给同类型的另一个联合体变量
14 union MyUnion un2 = un1;
  • 2、数据位操作

 1 #include<stdio.h>
2 typedef struct
3 {
4 unsigned char bit0:1;
5 unsigned char bit1:1;
6 unsigned char bit2:1;
7 unsigned char bit3:1;
8 unsigned char bit4:1;
9 unsigned char bit5:1;
10 unsigned char bit6:1;
11 unsigned char bit7:1;
12 }bitValue;
13
14 typedef union
15 {
16 unsigned char bytedata;
17 bitValue bitdata;
18 }regValue;
19
20 int main()
21 {
22 regValue data;
23 data.bytedata= 0x5A;
24 printf("%d",data.bitdata.bit5); //读取第6位
25 data.bitdata.bit7 = 1; //修改第8位
26 return 0;
27 }

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

  • 3、和struct嵌套使用

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

 1 struct tvFeature    //电视属性
2 {
3 char *logo; //品牌
4 int price; //价格
5 int screensize //屏幕尺寸
6 int resolution //分辨率
7 }tvFeature;
8 struct tvFeature tvfeature;
9
10 struct airFeature //空调属性
11 {
12 char *logo; //品牌
13 int price; //价格
14 int coldcapacity;//制冷量
15 int hotcapacity;//制热量
16 }airFeature;
17 struct airFeature airfeature;

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

 1 struct homeappliancesFeature  //电器属性
2 {
3 char *logo; //品牌
4 int price; //价格
5 int screensize //屏幕尺寸
6 int resolution //分辨率
7 int coldcapacity;//制冷量
8 int hotcapacity;//制热量
9 }homeappliancesFeature;
10
11 struct homeappliancesFeature tvfeature;
12 struct homeappliancesFeature airfeature;

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

 1 struct tvFeature    //电视属性
2 {
3 int screensize //屏幕尺寸
4 int resolution //分辨率
5 }tvFeature;
6 struct airFeature //空调属性
7 {
8 int coldcapacity;//制冷量
9 int hotcapacity;//制热量
10 }airFeature;
11
12 struct homeappliancesFeature //电器属性
13 {
14 char *logo; //品牌
15 long country; //国家
16 union
17 {
18 struct tvFeature tvST;
19 struct airFeature airST;
20 };
21 };
22 struct homeappliancesFeature tvfeature;
23 struct homeappliancesFeature airfeature;

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

  • 4、数据复制

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

 1 typedef union
2 {
3 uint8 data8[4];
4 uint32 data32;
5 }dataType;
6
7 uint32 sendData = 0x5A5AA5A5;
8 uint32 receiveData;
9 dataType commSend;
10 void main(void)
11 {
12 uint8 commData[128];
13 //数据复制
14 commData.data32 = sendData;
15 //发送数据,字节复制,不需要再将commData.data32单独移位拆分
16 commData[0]= commSend.data8[0];
17 commData[1]= commSend.data8[1];
18 commData[2]= commSend.data8[2];
19 commData[3]= commSend.data8[3];
20
21 //读取数据时,字节复制,不需要再将已经读取到的4个单字节数据拼接
22 receiveData = commData.data32;
23 }
  • 5、分时发送不同帧格式数据

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

 1 typedef struct
2 {
3 uint8 head; //帧头格式相同
4 union //中间数据格式不一样
5 {
6 struct //payloadType1
7 {
8 uint8 cmd;
9 uint8 type;
10 uint8 data[5];
11 uint8 check;
12 }msgType1;
13
14 struct //payloadType2
15 {
16 uint16 cmd;
17 uint8 data[8];
18 uint16 check;
19 }msgType2;
20
21 uint8 data[10]; //payloadType3
22 } payloadType;
23 uint8 end; //帧尾格式相同
24 }frameType;

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

1、数据大小端

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

 1 #include<stdio.h>
2 union Un
3 {
4 int i;
5 char c;
6 };
7 union Un un;
8
9 int main()
10 {
11 un.i = 0x11223344;
12 if (un.c == 0x11)
13 {
14 printf("大端\n");
15 }
16 else if (un.c == 0x44)
17 {
18 printf("小端\n");
19 }
20 }

2、指针方式访问

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

 1 #include<stdio.h>
2 typedef struct
3 {
4 unsigned char a;
5 int b;
6 }stValue1;
7
8 typedef struct
9 {
10 int c;
11 unsigned char d[10];
12 double e;
13 }stValue2;
14
15 //联合体成员定义为指针成员
16 union Un
17 {
18 stValue1 *ptrSt1;
19 stValue2 *ptrSt2;
20 };
21
22 int main()
23 {
24 union Un *info;
25 info->ptrSt1->a = 5;
26 info->ptrSt2->e = 9.7f;
27 }

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


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

在实际应用中联合体union的妙用的更多相关文章

  1. C和C++中结构体(struct)、联合体(union)、枚举(enum)的区别

    C++对C语言的结构.联合.枚举 这3种数据类型进行了扩展. 1.C++定义的结构名.联合名.枚举名 都是 类型名,可以直接用于变量的声明或定义.即在C++中定义变量时不必在结构名.联合名.枚举名 前 ...

  2. C语言中的联合体union所占内存方式

     当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union).在C Programming Language 一书中对于联合体是这么描述的:      1)联合体是一个结构:    ...

  3. 联合体union和大小端(big-endian、little-endian)

    1.联合体union的基本特性——和struct的同与不同 union,中文名“联合体.共用体”,在某种程度上类似结构体struct的一种数据结构,共用体(union)和结构体(struct)同样可以 ...

  4. 结构体struct、联合体union、枚举类型enum

    1.c语言中的类型 1)内置类型——char,short,int,float,double: 2)用户自定义类型(UDT)——struct结构体,union联合体,enum枚举类型 2.内存对齐 2. ...

  5. Boost--variant (C++中的union)

    union联合体类型的问题 只能用于内部类型,这使得union在C++中几乎没有用 所以boost提供了variant,相当于是C++中的union #include "boost/vari ...

  6. 联合体union的详解

    1.概述 联合体union的定义方式与结构体一样,但是二者有根本区别. 在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和.而在“联合”中,各成员共享一段内存空间,一个联合变量的长度 ...

  7. 联合体union用在何处?

    程序设计刚開始学习的人在学习时,总想问:"这个东东有什么用?"于是,在建设有关的教学资源时,也便总从这个角度,试图给出一些案例,这是一个将刚開始学习的人作为教学目标人群的人该干的事 ...

  8. 关于联合体union的详细解释

    1.概述 联合体union的定义方式与结构体一样,但是二者有根本区别. 在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和.而在“联合”中,各成员共享一段内存空间,一个联合变量的长度 ...

  9. Union的妙用和注意

    对于Union我用的比较少,最近一段时间大多使用Lua,所以复习一下Union Union是共用体,顾名思义,公用一块内存 一块内存不同的访问方式 // 1.数组的便捷访问 // 一块内存两种等价的访 ...

  10. C++中使用union的几点思考(转)

    C++中使用union的几点思考 大卫注:这段时间整理旧资料,看到一些文章,虽然讲的都是些小问题,不大可能用到,但也算是一个知识点,特整理出来与大家共享.与此相关的那篇文章的作者的有些理解是错误的,我 ...

随机推荐

  1. 从Java 9 到 Java 17 新特性梳理

    Java 9 新的创建集合的方法  // [1, 2, 3, 4]  List<Integer> integers = List.of(1, 2, 3, 4);  // {1,2,3}   ...

  2. 官方使用logstash同步Mysql数据表到ES的摘抄

    官方文档地址:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-jdbc.html#plugins-inputs-jdbc ...

  3. 迁移一个仓库到新的Gitlab

    一般这种迁移,要注意旧仓库的提交历史等信息也要同步到新的仓库. 先使用如下命令克隆老的: git clone --bare git@gitlab.test1.com:f2e/test.git 新仓库创 ...

  4. day42-反射01

    Java反射01 1.反射(reflection)机制 1.1反射机制问题 一个需求引出反射 请看下面问题: 根据配置文件 re.properties 指定信息,创建Cat对象并调用方法hi clas ...

  5. Module Federation 模块联邦 在Vue3中使用Vue2搭建的微服务

    前言: 备注:本文基于对webpack Module Federation有一定了解的情况下 一般情况下使用模块联邦都是会使用相同的版本,如Vue2的组件时在Vue2中使用,但我为什么会在Vue3项目 ...

  6. 洛谷P2860 [USACO06JAN]Redundant Paths G (tarjan,边双缩点)

    本题的大意就是加最少的边使得图成为边双. 多举例子,画图分析可得:最终答案就是叶子节点(度数为1的点)的个数加1在除以2. 那么我们的目的就转化为找叶子节点: 首先通过tarjan找到割边,再dfs将 ...

  7. get,post,put,delete四种基础方法对应增删改查

    PUT,DELETE,POST,GET四种基础方法对应增删改查 1.GET请求会向数据库发索取数据的请求,从而来获取信息,该请求就像数据库的select操作一样,只是用来查询一下数据,不会修改.增加数 ...

  8. Vue3 JS 与 SCSS 变量相互使用

    在开发中会遇到如下需求: JS 中使用 SCSS 变量.如在 scss 中定义了一个颜色,el-menu 组件使用该颜色作为背景色,此时需要获取 scss 变量,通过 background-color ...

  9. Vue3 SFC 和 TSX 方式调用子组件中的函数

    在开发中会遇到这样的需求:获取子组件的引用,并调用子组件中定义的方法.如封装了一个表单组件,在父组件中需要调用这个表单组件的引用,并调用这个表单组件的校验表单函数或重置表单函数.要实现这个功能,首先要 ...

  10. 齐博x1钩子自动添加频道参数变量

    频道或插件,增加功能的时候,可能要在后台增加开关参数.这个时候只需要增强对应的接口文件即可,比如创建这样一个文件\application\shop\ext\setting_get\give_jifen ...