vlambda博客
学习文章列表

C语言联合体(union)的使用方法及其本质-union

转载自:https://blog.csdn.net/sizhouqun_84342712/article/details/53187106

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

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所占内存空间大小

#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包含的变量)!但是,没开辟就不能用了?当然也能用!  写个小测试:

#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]);
}