网站开发专业就业指导,做网站的软件项目进度计划,电商是啥意思是做什么的,asp网站开发实训联合体#xff08;union#xff09;的使用方法及其本质
转自#xff1a;https://blog.csdn.net/huqinwei987/article/details/23597091
有些基础知识快淡忘了#xff0c;所以有必要复习一遍#xff0c;在不借助课本死知识的前提下做些推理判断#xff0c;温故知新。
1…联合体union的使用方法及其本质
转自https://blog.csdn.net/huqinwei987/article/details/23597091
有些基础知识快淡忘了所以有必要复习一遍在不借助课本死知识的前提下做些推理判断温故知新。
1.联合体union的基本特性——和struct的同与不同
union中文名“联合体、共用体”在某种程度上类似结构体struct的一种数据结构共用体(union)和结构体(struct)同样可以包含很多种数据类型和变量。
不过区别也挺明显
结构体(struct)中所有变量是“共存”的——优点是“有容乃大”全面缺点是struct内存空间的分配是粗放的不管用不用全分配。
而联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”但优点是内存使用更为精细灵活也节省了内存空间。
2.双刃剑——多种访问内存途径共存
一个例子了然
//example
#includestdio.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呢~
这种逻辑上的错误是很难找出来的只有当数据类型相去甚远的时候稍好出个乱码什么的很容易发现错误。
#includestdio.h
union var{long int l;int i;
};
int main(){union var v;v.l 5;printf(%ld\n,v.l);v.i 6;
}3.联合体union和大小端big-endian、little-endian
下边示范了一种用途代表四个含义的四个变量但是可以用一个int来操作直接int赋值无论内存访问指针大小的整数倍访问才有效率还是时间复杂度一次和四次的区别而且这四次有三次都是不整齐的地址都会低一些。
#includestdio.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到底总共有多大根据一些小常识做个不严谨不高深的基础版验证吧。
根据分配栈空间的时候内存地址基本上是连续的至少同类型能保证在一起连续就说明我如果弄三个结构体出来他们三个地址应该连着看一下三个地址的间隔就知道了。
#includestdio.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-6028 60-3828 算错了我说的可是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和structC不就是玩地址么所以使用C灵活也容易犯错
没错union的成员变量是相当于开辟了几个访问途径即union包含的变量但是没开辟的访问方式就不能用了当然也能用
写个小测试
#includestdio.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打印字符串abcprintf(%c %c %c %c %c %c\n,c[0],c[1],c[2],c[3],c[4],c[5]);
}一个例子了然我的结构体只定义了int和double“接口”只要我获得地址往里边扔什么数据谁管得到这就是C语言不止union的本质——只管开辟一段空间。
但是你获取地址并访问和存取的数据最好确定是合法语法合理用途符合的地址不然虽然能操作后患无穷C的头疼之处可能出了问题你都找不到。
补充
补充1
解决一下捧场网友的困惑。
关于“有名”与“无名”联合体在结构体内所占空间的问题其实这和是不是结构体无关只和“有名”、“无名”有关而且有名无名也是表象其实是声明类型与定义变量的区别看例子直接打印
#include stdio.h
struct s1{union u{int i;};struct ss1{int i;};
};struct s2{union{int i;};struct{int i2;};
};struct s3{//the same to s2union su3{int i;}su33;struct ss3{int i;}ss33;
};union su4{int i;
};
struct ss4{int i;
};
struct s4{//the same to s3union su4 su44;struct ss4 ss44;
};
struct s5{//the same to s1union su4;struct ss4;
};struct s6{//the same to s1union{int;};struct{int;};
};main(){struct s1 sVal1;struct s2 sVal2;struct s3 sVal3;struct s4 sVal4;struct s5 sVal5;struct s6 sVal6;printf(sVal1s size:%d\n,sizeof(sVal1));printf(sVal1:%p\t%d\n,sVal1,sVal1);printf(sVal2s size:%d\n,sizeof(sVal2));printf(sVal2:%p\t%d\n,sVal2,sVal2);printf(sVal3s size:%d\n,sizeof(sVal3));printf(sVal3:%p\t%d\n,sVal3,sVal3);printf(sVal4s size:%d\n,sizeof(sVal4));printf(sVal4:%p\t%d\n,sVal4,sVal4);printf(sVal5s size:%d\n,sizeof(sVal5));printf(sVal5:%p\t%d\n,sVal5,sVal5);printf(sVal6s size:%d\n,sizeof(sVal6));printf(sVal6:%p\t%d\n,sVal6,sVal6);
}地址供参考主要看size分别为088800
s1只有类型声明没有定义没有变量自然就没有空间占用s5同)。
s2这种写法就是直接定了union和struct。
s3和s2的区别只是s2过于简化s3的意思是既声明了union su3又定义了这个类型对应的变量su33.
s4和s5作为对比为了更好的说明这一点。s5也是纯“贴”表达式没声明变量。
s6乍一看类似s2其实union内部没有具体变量也是为了做对比的。和s1的不同之处是一个是外部的union一个是内部的int都是干声明不定义所以没成员不占用空间。
类型就是类型和是不是结构体、联合体无关的你的“int i;”中i不就是个变量吗如果换成int;结果相同这就是s6。
另外这种做法编译的时候GCC会给你在相应的行做出提示“union_with_name.c:49: 警告没有声明任何东西”
很多人表示打印结果不一样我试过很多次不一样的环境都是一样的。
以上仅属于个人心得和推测重点在于学习思维和推理验证过程不保证正确性与权威性。有兴趣讨论或者有发现错误的欢迎留言交流指正。