联合体(union)的使用方法及其本质
转自:http://blog.csdn.net/huqinwei987/article/details/23597091
有些基础知识快淡忘了,所以有必要复习一遍,在不借助课本死知识的前提下做些推理判断,温故知新。
1.联合体union的基本特性——和struct的同与不同
union,中文名“联合体、共用体”,在某种程度上类似结构体struct的一种数据结构,共用体(union)和结构体(struct)同样可以包含很多种数据类型和变量。
不过区别也挺明显:
结构体(struct)中所有变量是“共存”的——优点是“有容乃大”,全面;缺点是struct内存空间的分配是粗放的,不管用不用,全分配。
而联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”;但优点是内存使用更为精细灵活,也节省了内存空间。
2.双刃剑——多种访问内存途径共存
一个例子了然:
- //example
- #include<stdio.h>
- union var{
- long int l;
- int i;
- };
- main(){
- union var v;
- v.l = 5;
- printf("v.l is %d\n",v.i);
- v.i = 6;
- printf("now v.l is %ld! the address is %p\n",v.l,&v.l);
- printf("now v.i is %d! the address is %p\n",v.i,&v.i);
- }
- 结果:
- v.l is 5
- now v.l is 6! the address is 0xbfad1e2c
- now v.i is 6! the address is 0xbfad1e2c
所以说,管union的叫共用体还真是贴切——完全就是共用一个内存首地址,并且各种变量名都可以同时使用,操作也是共同生效。如此多的access内存手段,确实好用,不过这些“手段”之间却没法互相屏蔽——就好像数组+下标和指针+偏移一样。
上例中我改了v.i的值,结果v.l也能读取,那么也许我还以为v.l是我想要的值呢,因为上边提到了union的内存首地址肯定是相同的,那么还有一种情况和上边类似:
一个int数组变量a,一个long int(32位机中,long int占4字节,与int相同)变量b,我即使没给int变量b赋值,因为数据类型相同,我使用int变量b也完全会拿出int数组a中的a[0]来,一些时候一不小心用上,还以为用的就是变量b呢~
这种逻辑上的错误是很难找出来的(只有当数据类型相去甚远的时候稍好,出个乱码什么的很容易发现错误)。
PS:感谢热心网友的提醒“在union定义结束时加分号”,其实是可以不加的,因为他不在主函数内,不是执行的语句,如果是主函数内声明的union就必须加分号了,在主函数内不加分号就涉及到基础常识了——没有分号隔开怎能叫一句。
3.联合体union和大小端(big-endian、little-endian):
- #include<stdio.h>
- union var{
- char c[4];
- int i;
- };
- int main(){
- union var data;
- data.c[0] = 0x04;//因为是char类型,数字不要太大,算算ascii的范围~
- data.c[1] = 0x03;//写成16进制为了方便直接打印内存中的值对比
- data.c[2] = 0x02;
- data.c[3] = 0x11;
- //数组中下标低的,地址也低,按地址从低到高,内存内容依次为:04,03,02,11。总共四字节!
- //而把四个字节作为一个整体(不分类型,直接打印十六进制),应该从内存高地址到低地址看,0x11020304,低位04放在低地址上。
- printf("%x\n",data.i);
- }
结果:
11020304
证明我的32位linux是小端(little-endian)
4.联合体union所占内存空间大小:
前边说了,首先,union的首地址是固定的,那么,union到底总共有多大?根据一些小常识,做个不严谨不高深的基础版验证吧。
根据:分配栈空间的时候内存地址基本上是连续的,至少同类型能保证在一起,连续就说明,我如果弄三个结构体出来,他们三个地址应该连着,看一下三个地址的间隔就知道了。
- #include<stdio.h>
- union sizeTest{
- int a;
- double b;
- };
- main(){
- union sizeTest unionA;
- union sizeTest unionB;
- union sizeTest unionC;
- printf("the initial address of unionA is %p\n",&unionA);
- printf("the initial address of unionB is %p\n",&unionB);
- printf("the initial address of unionC is %p\n",&unionC);
- }
打印,可以看到结果:
the initial address of unionA is 0xbf9b8df8
the initial address of unionB is 0xbf9b8e00
the initial address of unionC is 0xbf9b8e08
很容易看出,8,0,8,这间隔是8字节,按double走的。
怕不保险,再改一下,把int改成数组,其他不变:
- union sizeTest{
- int a[10];
- double b;
- };
打印
the initial address of unionA is 0xbfbb7738
the initial address of unionB is 0xbfbb7760
the initial address of unionC is 0xbfbb7788
88-60=28
60-38=28
算错了?我说的可是16进制0x。那么0x28就是40个字节,正好是数组a的大小。
似乎忘了一个功能——sizeof()
用sizeof直接看,就知道union的大小了
- printf("the sizeof of unionA is %d\n",sizeof(unionA));
- printf("the sizeof of unionB is %d\n",sizeof(unionB));
- printf("the sizeof of unionC is %d\n",sizeof(unionC));
- printf("the sizeof of union is %d\n",sizeof(union sizeTest));
5.联合体union适用场合:
有了前边那个验证,基本可以确认,union的内存是照着里边占地儿最大的那个变量分的。
也就可以大胆的推测一下,这种union的使用场合,是各数据类型各变量占用空间差不多并且对各变量同时使用要求不高的场合(单从内存使用上,我觉得没错)。
像上边做的第二个测试,一个数组(或者更大的数组int a[100]),和一个或者几个小变量写在一个union里,实在没什么必要,节省的空间太有限了,还增加了一些风险(最少有前边提到的逻辑上的风险)。所以,从内存占用分析,这种情况不如直接struct。
不过话说回来,某些情况下虽然不是很节约内存空间,但是union的复用性优势依然存在啊,比如方便多命名,这种“二义性”,从某些方面也可能是优势。这种方法还有个好处,就是某些寄存器或通道大小有限制的情况下,可以分多次搬运。
6.本质&进阶:
根据union固定首地址和union按最大需求开辟一段内存空间两个特征,可以发现,所有表面的定义都是虚的,所谓联合体union,就是在内存给你划了一个足够用的空间,至于你怎么玩~它不管~!(何止是union和struct,C不就是玩地址么,所以使用C灵活,也容易犯错)
没错,union的成员变量是相当于开辟了几个接口(即union包含的变量)!但是,没开辟就不能用了?当然也能用!
写个小测试:
- #include<stdio.h>
- union u{
- int i;
- double d;//这个union有8字节大小
- };
- main(){
- union u uu;
- uu.i = 10;
- printf("%d\n",uu.i);
- char * c;
- c = (char *)&uu;//把union的首地址赋值、强转成char类型
- c[0] = 'a';
- c[1] = 'b';
- c[2] = 'c';
- c[3] = '\0';
- c[4] = 'd';
- c[5] = 'e';
- //最多能到c[7]
- printf("%s\n",c);//利用结束符'\0'打印字符串"abc"
- printf("%c %c %c %c %c %c\n",c[0],c[1],c[2],c[3],c[4],c[5]);
- }
一个例子了然,我的结构体只定义了int和double“接口”,只要我获得地址,往里边扔什么数据谁管得到?这就是C语言的强大,这就是union的本质——只管开辟一段空间。
有些东西,熟悉编译原理和编译器工作过程的话,解决会更容易点,虽然我现在这方面技能不太强,不过一般问题也足够分析了。
====================================================================================================================================
补充:
补充1:
解决一下捧场网友的困惑。
关于“有名”与“无名”联合体在结构体内所占空间的问题,其实这和是不是结构体无关,只和“有名”、“无名”有关,而且有名无名也是表象,其实是声明类型与定义变量的区别,看例子,直接打印,
- #include <stdio.h>
- struct s1{
- union u{
- int i;
- };
- struct ss1{
- int i;
- };
- };
- struct s2{
- union{
- int i;
- };
- struct{
- int i;
- };
- };
- struct s3{//the same to s2
- union su3{
- int i;
- }su33;
- struct ss3{
- int i;
- }ss33;
- };
- union su4{
- int i;
- };
- struct ss4{
- int i;
- };
- struct s4{//the same to s3
- union su4 su44;
- struct ss4 ss44;
- };
- struct s5{//the same to s1
- union su4;
- struct ss4;
- };
- struct s6{//the same to s1
- union{
- int;
- };
- struct{
- int;
- };
- };
- main(){
- struct s1 sVal1;
- struct s2 sVal2;
- struct s3 sVal3;
- struct s4 sVal4;
- struct s5 sVal5;
- struct s6 sVal6;
- printf("sVal1's size:%d\n",sizeof(sVal1));
- printf("sVal1:%p\t%d\n",&sVal1);
- printf("sVal2's size:%d\n",sizeof(sVal2));
- printf("sVal2:%p\t%d\n",&sVal2);
- printf("sVal3's size:%d\n",sizeof(sVal3));
- printf("sVal3:%p\t%d\n",&sVal3);
- printf("sVal4's size:%d\n",sizeof(sVal4));
- printf("sVal4:%p\t%d\n",&sVal4);
- printf("sVal5's size:%d\n",sizeof(sVal5));
- printf("sVal5:%p\t%d\n",&sVal5);
- printf("sVal5's size:%d\n",sizeof(sVal5));
- printf("sVal5:%p\t%d\n",&sVal5);
- }
地址供参考,主要看size,分别为:
0,8,8,8,0,0
s1只有类型,没有变量,没有变量自然就没有空间(s5同)。
类型就是类型,和是不是结构体、联合体无关的,你的“int i;”中i不就是个变量吗?如果换成int;结果相同(这就是s6)。
s4和s5的做法能帮助排除干扰,将子结构体与联合体声明在外,内部直接引用,4是定义了变量,5什么都没做。
另外,这种做法编译的时候GCC会给你在相应的行做出提示“union_with_name.c:49: 警告:没有声明任何东西”
========================================================================================================================================================================================================================================================================
以上仅属于个人心得和推测,重点在于学习思维和推理验证过程,不保证正确性与权威性。有兴趣讨论或者有发现错误的,欢迎留言交流指正。
觉得好的,欢迎转载并注明出处。
http://blog.csdn.net/huqinwei987/article/details/23597091
本人博客会根据个人经验升级情况随时补充修改。
联合体(union)的使用方法及其本质的更多相关文章
- C语言联合体(union)的使用方法及其本质-union
转载自:https://blog.csdn.net/si_zhou_qun_84342712/article/details/53187106 1.联合体union的基本特性——和struct的同与不 ...
- 联合体union用在何处?
程序设计刚開始学习的人在学习时,总想问:"这个东东有什么用?"于是,在建设有关的教学资源时,也便总从这个角度,试图给出一些案例,这是一个将刚開始学习的人作为教学目标人群的人该干的事 ...
- 联合体union和大小端(big-endian、little-endian)
1.联合体union的基本特性——和struct的同与不同 union,中文名“联合体.共用体”,在某种程度上类似结构体struct的一种数据结构,共用体(union)和结构体(struct)同样可以 ...
- C#扫盲之:带你掌握C#的扩展方法、以及探讨扩展方法的本质、注意事项
1.为什么需要扩展方法 .NET3.5给我们提供了扩展方法的概念,它的功能是在不修改要添加类型的原有结构时,允许你为类或结构添加新方法. 思考:那么究竟为什么需要扩展方法呢,为什么不直接修改原有类型呢 ...
- Linq中Union与Contact方法用法对比
文章一开始,我们来看看下面这个简单的实例. 代码片段1: int[] ints1 = { 2, 4, 9, 3, 0, 5, 1, 7 }; int[] ints2 = { 1, 3, 6, 4, 4 ...
- 网络工程 POST与GET请求方法的本质区别
POST与GET请求方法的本质区别: 第一:GET用于信息获取,它是安全的(这里安全的含义是指非修改信息),而POST是用于修改服务器上资源的请求 第二:GET请求的数据会附在URL之后,而POST把 ...
- 结构体struct、联合体union、枚举类型enum
1.c语言中的类型 1)内置类型——char,short,int,float,double: 2)用户自定义类型(UDT)——struct结构体,union联合体,enum枚举类型 2.内存对齐 2. ...
- 联合体union的详解
1.概述 联合体union的定义方式与结构体一样,但是二者有根本区别. 在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和.而在“联合”中,各成员共享一段内存空间,一个联合变量的长度 ...
- C语言中的联合体union所占内存方式
当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union).在C Programming Language 一书中对于联合体是这么描述的: 1)联合体是一个结构: ...
随机推荐
- BZOJ 4698: Sdoi2008 Sandy的卡片
4698: Sdoi2008 Sandy的卡片 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 106 Solved: 40[Submit][Stat ...
- 【BZOJ-3039&1057】玉蟾宫&棋盘制作 悬线法
3039: 玉蟾宫 Time Limit: 2 Sec Memory Limit: 128 MBSubmit: 753 Solved: 444[Submit][Status][Discuss] D ...
- 为什么npm install在安装时会多安装很多依赖包
比如我安装gulp时,会多出很多无用的包,如下图: 经过查询,原来是npm升级了导致的,在npm3.0以上的版本,包的依赖不再安装在每个架包的node_modules文件夹内,而是安装在顶层的node ...
- 数据结构作业——ギリギリ eye(贪心+优先队列/贪心+并查集)
ギリギリ eye Description A.D.1999,由坠落地球的“谜之战舰”带来的 Over Technology,揭示了人类历史和远古文明之间的丝丝联系, 促使人类终止彼此间的战争,一方面面 ...
- codevs 1036 商务旅行(Targin求LCA)
传送门 Description 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任意 ...
- Linux/Mac/Shell常用命令
常用命令 · ls 查看当前目录下的文件 · cd 进入某目录 · cd - 跳转回前一目录 · cd ~ 进入当前用户个人目录 · pwd 输出当前所在路径 · mkdir 新建文件夹 · touc ...
- photoshop拾色器如何恢复默认?
今天在做设计图的时候,遇到一个问题,当时就把我给整蒙了. 问题是这样的,ps的调色器变成了这样,如下: 本来应该是这样: 可能有人已经看出两张图的不同之处了. 但是我当时忙的不得了,恩是不知道哪里除了 ...
- struts2+hibernate 项目实战:图书管理系统
经典项目,练手必备. 图书管理系统 需求分析(大致,并不专业):1.需要有用户管理: 1.1 用户注册: 1.2 用户登录: 1.3 用户信息修改: 1.4 用户修改密码: 2.需要有书本管理: 2. ...
- eclipse中SSH三大框架环境搭建<三>
相关链接: eclipse中SSH三大框架环境搭建<一> eclipse中SSH三大框架环境搭建<二> 引言:通过上两篇文章我们已经可以掌握struts2和spring的环境的 ...
- [译]ES6新特性:八进制和二进制整数字面量
原文:http://whereswalden.com/2013/08/12/micro-feature-from-es6-now-in-firefox-aurora-and-nightly-binar ...