引言
在C语言中,每个数据类型都有其特定的作用或使用场景。 前面我们详细的分析了数组,结构体,今天我们来讲讲共用体(union),所谓的共用体就是使用覆盖技术让数据成员之间相互覆盖,以实现不同数据成员共用同一段内存。先引出概念,接下来我们从共用体与其他的数据类型区别,到共用体类型如何定义,哪些场景会使用共用体等等,逐步分析。
共用体和结构体区别
共用体(union)和结构体(struct)在类型定义, 变量定义,使用方法上都非常相似:
//共用体类型定义union test{int achar b;};
变量的定义和使用:
//定义共用体变量t1union test t1;//使用共同体t1成员t1.a = 23;
那两者有什么区别呢?结构体好比是一个袋子,袋子里面装了各种各样的东西,每个东西都是独立存在的,占据了袋子体积; 而共用体的各个成员是一体的,彼此不独立,它们使用同一块内存空间,可以理解为:有时候是这个元素,有时候是另外一个元素(就像是一个多面人),有不同的形态,更准确的说是同一块内存空间有多种解释方式!通过以下示意图感受下:
![730f6ed3504186af2b40ba834bd60d14.png](https://i-blog.csdnimg.cn/blog_migrate/a85f1d151b09e8b67b4f8251c8f04791.jpeg)
接下来我们通过实例代码,来理解其本质区别:
#include //定义结构体类型struct test1{int a;char b;};//定义共用体类型union test2{int a;char b;};int main(void){ //定义结构体变量t1struct test1 t1; //定义共用体变量2union test2 t2;t1.a = 23;t2.a = 24; //打印变量元素printf("t1.b = %d.", t1.b);printf("t2.b = %d.", t2.b);//打印结构体t1元素的地址 printf("&t1.a = %p.", &t1.a);printf("&t1.b = %p.", &t1.b);//打印共用体t2元素地址printf("&t2.a = %p.", &t2.a);printf("&t2.b = %p.", &t2.b);return 0;}
说明:
1.我们分别定义了结构体变量t1, 和共用体变量t2
2. 初始化了结构体t1中的元素a, 赋值为23,但是元素b未初始化
3. 初始化了共用体t2中的元素a, 赋值为24,同样元素b未初始化
4. 之后我们分别打印了t1和t2成员的地址
接下来我们编译运行,看下结果:
![ffcb89b7b7febdffaf6faa4c3e977e57.png](https://i-blog.csdnimg.cn/blog_migrate/dab6550dcf25d043c13e45f44de54468.jpeg)
可以看到结构体t1的元素b未初始化,是一个随机值(因为我们定义的t1是局部变量,定义在栈上,如果不清楚什么是栈,什么是堆,以及它们的特性,可以查阅我之前的文章~)。
而共用体t2元素b未初始化, 但是它的值与元素a居然相同(这不是凑巧哦,不相信的小伙伴可以自己测试下,当然前提是a的初始化的值不能超过元素b的类型值域, 同时这里会涉及大小端的问题,这个之后会详细分析)。同时可以看到结构体t1的元素a和b的地址是不同的, 而共用体t2的元素a和b地址是完全一致!相信到这里大家能基本了解了共用体的特性: 成员之间共用一块内存空间,只是解析方式不同。
共用体的大小
经过上面的讲述,大家知道了共用体的成员其实是共享了一块内存空间,哪么当我们顶一个共用体类型时,它实际占用的内存空间是多少呢?
我们先举一个简单的例子:
#include union test{int a;char b;};int main(void){union test t1; //打印共用体成员的数据类型大小printf("sizeof(int) = %d.", sizeof(int));printf("sizeof(char) = %d.", sizeof(char));//打印共用体大小printf("sizeof(union test) = %d.", sizeof(union test));return 0;}
编译运行结果:
![0b3a9a544772eaa64e153af7223810d9.png](https://i-blog.csdnimg.cn/blog_migrate/8a6f80c5745ebc7e3a782f89f1f37b1f.jpeg)
可以看到共用体变量t1的内存大小为4字节, 即原则上,共用体大小取决于占据最多内存的成员的长度。需要注意这里用的“原则”一词,因为有时候需要考虑内存对齐问题。 我们通过以下例子来加深理解:
#include //通过__attribute__指令设置1字节对齐__attribute__((aligned(1)))struct mystruct{char a;int b;int c;int d;int e;double f;char g;short h;double i;float j;}__attribute__((packed));//通过__attribute__指令设置1字节对齐__attribute__((aligned(1)))union test{int a;char b;float c;double d;struct mystruct my1;}__attribute__((packed));int main(void){//定义了共用体变量t1union test t1; //打印了结构体大小printf("sizeof(struct mystruct) = %d.", sizeof(struct mystruct));//打印共用体大小printf("sizeof(union test) = %d.", sizeof(union test));return 0;}
分析:
1. 首先我们定义了结构体类型mystruct, 并使用__attribute__指令设置为1字节对齐
2. 定义了共用体类型,其中一个成员是上面定义的结构体mystruct类型, 同时也使用__attribute__指令设置为1字节对齐
3. 定义了共用体变量t1
4. 分别打印了结构体mystruct类型大小,和共用体test 类型大小
OK, 编译运行:
![f961e2d4f61c1683bf79a8eed0a373f9.png](https://i-blog.csdnimg.cn/blog_migrate/cfc0853fd4ee16182ac4ebd19ff7ad8a.jpeg)
我们可以看到共用体的大小就等于成员my1(结构体类型)的大小,再次验证了上面的结论。
共用体的访问方式
上面我们举得的例子都是使用下标式访问共用体成员,还是和数组,结构体一样, 下标式访问知识编译器提供的一种语法糖, 方便开发人员使用和理解, 但是我们需要了解其本质: 共享一块内存,只是解析方式不同。 因此我们可以通过指针方式来访问,这也是真正理解共用体的判断标准。
#include union test{int a;float b;};int main(void){union test t1; //下标式访问t1.a = 1123477094;printf("t1.b = %f.", t1.b); //指针式访问int a = 1123477094;printf("指针方式: t1.b = %f.", *(float *)&a);return 0;}
编译运行:
![988423c79931cf2ad638f2539f1bec8f.png](https://i-blog.csdnimg.cn/blog_migrate/0c8691bc62e0195f94dde68dbde40a67.jpeg)
从结果再次验证共用体实质:共享一块内存,只是解析方式不同。
总结
通过以上分析,我们了解了共用体的实质,如何定义和使用,以及与结构体的区别等。 之后我会讲解共用体的应用场景,尤其是如何使用共用体测试系统大小端,这也是面试和笔试中出现的概率非常高的题目,如果你对大小端的概念不是很清楚,或者想了解有哪些办法测试系统的大小端,欢迎转发,收藏,关注,后续内容更加精彩哟~
![2e53203bfb9270ba42ab96ef1bf3ad05.png](https://i-blog.csdnimg.cn/blog_migrate/71a89819d38214ec08e16fe84ad9dce5.jpeg)