转自: http://blog.csdn.net/Hu qinwei 987/article/details/23597091
有些基础知识很快就会忘记,需要复习一遍,在不借教科书而死知识的前提下进行推理判断,温故知新。
1 .复合union的基本特性与——和struct的相同
union,中文名“联合体,联合体”,在一定程度上类似于结构struct的数据结构,联合体(union )和结构struct )同样可以包含许多数据类型和变量。
但区别很明显:
结构(struct )中所有变量都是“共存”的——的优点是“有容乃大”,是全面的; 缺点是结构内存空间分配粗放,无论是否使用,都是全分配。
另一方面,在联合体(union )中各变量为“互斥”的——的缺点是“包容”不够。 但是,内存的使用更加细致灵活,具有还可以节约内存空间的优点。
2 .双刃剑——多条存取存储器路径共存
举个例子:
//example
#包含
unionvar{
长整型;
inti;
(;
主()。
unionvarv;
v.l=5;
printf(v.lis%d(n ),v.i );
v.i=6;
printf(Nowv.lis%LD! theaddressis%pn ',v.l,v.l;
printf(Nowv.IIS%d! theaddressis%pn ',v.i,v.i;
}
结果:
v.lis5
nowv.lis6! theaddressis0xbfad1e2c
nowv.iis6! theaddressis0xbfad1e2c
也就是说,管道union的共同体就是共享——这个真正的内存的起始地址,可以同时使用各种各样的变量名,操作也很有效。 虽然许多此类存取存储器装置确实易于使用,但它们的“装置”之间的屏蔽333到354并不相互屏蔽。 就像数组下标和指针错位了一样。
在上面的示例中,更改了v.i的值。 结果,也能读v.l。 那么,v.l可能是我想要的值。 如上所述,union的内存起始地址一定是相同的,所以另一种情况与上面类似。
int数组变量a和longint (在32位计算机上,longint有4个字节,与int相同) )变量b。 即使我没有给int变量b赋值,但由于数据类型相同,所以即使使用int变量b,我也会完全检索int数组a的a[0]。 有时不小心使用了,还以为是使用了变量b。
这种逻辑错误很难找到(只有在数据类型分开的情况下好一点,出现乱码等时容易发现错误)。
PS:感谢热心网友的提醒。 “union定义结束时加分”,其实不加也可以。 他不在主函数内,所以不是执行的语句。 如果是在主函数内声明的union,就必须加分。 在主函数内不加分与基础常识有关。 ——没有用分号隔开,怎么能叫一句呢?
3 .联合union和大小结尾(big-endian,little-endian ) :
#包含
unionvar{
charc[4];
inti;
(;
intmain ()。
unionvardata;
data.c[0]=0x04; 因为是char类型,所以数字不要太大,选在ascii的范围~
data.c[1]=0x03; //用十六进制格式写,以便于直接打印内存中的值比较
data.c[2]=0x02;
data.c[3]=0x11;
//排列中下标低的,地址也低,按照地址从低到高的顺序,存储器内容是04、03、02、11的顺序。 总共4字节!
//另一方面,整个4字节(无论类型,直接打印16进制),从内存的高地址到低地址来看,0x11020304、低04应该放在低地址。
printf(%x(n ),data.i );
}
结果:
11020304
证明我的32位linux是小端序
4 .联盟union占用的内存空间大小:
如上所述,首先,union的起始地址是固定的。 那么,union到底有多大? 让我们根据一些小常识,来验证一下不够严密的基础版吧。
根据:分配堆栈空间时,存储器地址基本上是连续的。 至少可以保证同类型在一起。 如果是连续的话就说明。 如果我做了三个结构体出来,他们应该有三个地址相连。 看看三个地址的间隔就知道了。
#包含
unionsizeTest{
inta;
doubleb
(;
主()。
unionsizeTestunionA;
un
ion sizeTest unionB;union sizeTest unionC;
printf("the initial address of unionA is %pn",&unionA);
printf("the initial address of unionB is %pn",&unionB);
printf("the initial address of unionC is %pn",&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 %dn",sizeof(unionA));
printf("the sizeof of unionB is %dn",sizeof(unionB));
printf("the sizeof of unionC is %dn",sizeof(unionC));
printf("the sizeof of union is %dn",sizeof(union sizeTest));
5.联合体union适用场合:
有了前边那个验证,基本可以确认,union的内存是照着里边占地儿最大的那个变量分的。
也就可以大胆的推测一下,这种union的使用场合,是各数据类型各变量占用空间差不多并且对各变量同时使用要求不高的场合(单从内存使用上,我觉得没错)。
像上边做的第二个测试,一个数组(或者更大的数组int a[100]),和一个或者几个小变量写在一个union里,实在没什么必要,节省的空间太有限了,还增加了一些风险(最少有前边提到的逻辑上的风险)。所以,从内存占用分析,这种情况不如直接struct。
不过话说回来,某些情况下虽然不是很节约内存空间,但是union的复用性优势依然存在啊,比如方便多命名,这种“二义性”,从某些方面也可能是优势。这种方法还有个好处,就是某些寄存器或通道大小有限制的情况下,可以分多次搬运。
6.本质&进阶:
根据union固定首地址和union按最大需求开辟一段内存空间两个特征,可以发现,所有表面的定义都是虚的,所谓联合体union,就是在内存给你划了一个足够用的空间,至于你怎么玩~它不管~!(何止是union和struct,C不就是玩地址么,所以使用C灵活,也容易犯错)
没错,union的成员变量是相当于开辟了几个接口(即union包含的变量)!但是,没开辟就不能用了?当然也能用!
写个小测试:
#include
union u{
int i;
double d;//这个union有8字节大小
};
main(){
union u gddxz;
gddxz.i = 10;
printf("%dn",gddxz.i);
char * c;
c = (char *)&gddxz;//把union的首地址赋值、强转成char类型
c[0] = 'a';
c[1] = 'b';
c[2] = 'c';
c[3] = ' ';
c[4] = 'd';
c[5] = 'e';
//最多能到c[7]
printf("%sn",c);//利用结束符' '打印字符串"abc"
printf("%c %c %c %c %c %cn",c[0],c[1],c[2],c[3],c[4],c[5]);
}
一个例子了然,我的结构体只定义了int和double“接口”,只要我获得地址,往里边扔什么数据谁管得到?这就是C语言的强大,这就是union的本质——只管开辟一段空间。
有些东西,熟悉编译原理和编译器工作过程的话,解决会更容易点,虽然我现在这方面技能不太强,不过一般问题也足够分析了。
====================================================================================================================================
补充:
补充1:
解决一下捧场网友的困惑。
关于“有名”与“无名”联合体在结构体内所占空间的问题,其实这和是不是结构体无关,只和“有名”、“无名”有关,而且有名无名也是表象,其实是声明类型与定义变量的区别,看例子,直接打印,
#include
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:%dn",sizeof(sVal1));
printf("sVal1:%pt%dn",&sVal1);
printf("sVal2's size:%dn",sizeof(sVal2));
printf("sVal2:%pt%dn",&sVal2);
printf("sVal3's size:%dn",sizeof(sVal3));
printf("sVal3:%pt%dn",&sVal3);
printf("sVal4's size:%dn",sizeof(sVal4));
printf("sVal4:%pt%dn",&sVal4);
printf("sVal5's size:%dn",sizeof(sVal5));
printf("sVal5:%pt%dn",&sVal5);
printf("sVal5's size:%dn",sizeof(sVal5));
printf("sVal5:%pt%dn",&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
本人博客会根据个人经验升级情况随时补充修改。