联合体的声明
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫:共用体。
//联合类型的声明
union U {
char i;
int j;
};
int main()
{
//联合类型的定义
union U u;
return 0;
}
联合体的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
联合体共用一块空间
代码1:
union U {
char i;
int j;
};
int main()
{
union U u = {0};
printf("%p\n", &(u.i));
printf("%p\n", &(u.j));
printf("%p\n", &u);
return 0;
}
输出结果为
三个输出的结果都是相同的地址。
代码2:
union U {
char i;
int j;
};
int main()
{
union U u = { 0 };
printf("%zd\n", sizeof(u));
return 0;
}
输出结果为4,而一个整型变量的大小就是4。
代码3:
union U {
char i;
int j;
};
int main()
{
union U u = { 0 };
u.j = 0x11223344;
u.i = 0x55;
printf("%x\n", u.j);
return 0;
}
输出结果为11223355;说明我们赋值的44被修改了
什么原因导致上面三种情况出现了呢?
我们调试可以发现联合体的 j 确实赋值了相对应的值
因为是小端存储,所以在低地址,当我们继续向下调试的时候就会发现44被改为55,说明联合体的 i 存储在44的位置。
这也同时说明了他们共用一个地址,共用存储空间,这也解释了为什么刚刚创建的联合体大小是4了。
我们仔细分析就可以画出,u的内存布局图。
i代表刚刚的整型j,c代表刚刚的字符型i,un是刚刚的结构体。
联合体大小的计算
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
第二个规则要结合代码理解,比如:
union Un1 {
char c[5];
int i;
};
union Un2 {
short c[7];
int i;
};
上述代码Un1中 c 的最大对齐数应该是他的成员类型的对齐数,就是1,默认是8,那么取1。
i 的最大对齐数是4,默认是8,那么取4。
那么联合体的最大对齐数就是4,但是char c[5] 占5个字节的空间。这就是第二个规则的情况,最大成员大小是5,不是最大对齐数 4 的整数倍。所以要取最大对齐数的整数倍的大小的内存空间,就是 8 。
以同样的方法分析第二个联合体Un2的大小就是,最大对齐数是4,最大成员大小是14,所以最终的空间大小是16。
联合体使用场景
很简单的可以看出,联合体和结构体相比是大大节省了内存空间的。但是他的弊端也很明显,就是使用一个其中一个成员,其他成员的值可能也会被改变。
那么使用时就要注意,使用一个成员的时候不能在使用其他成员。
比如下面的场景:
我们要搞一个活动,要上线一个礼品兑换单,礼品兑换单中有三种商品:图书、杯子、衬衫。每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、页数
杯⼦:设计
衬衫:设计、可选颜色、可选尺寸
如果我们使用结构体的话
struct gift_list
{
//公共属性
int stock_number;// 库存量
double price; //定价
int item_type;//商品类型
//特殊属性
char title[20];//书名
char author[20];//作者
int num_pages;//页数
char design[30];//设计
int colors;//颜色
int sizes;//尺寸
};
实设计的很简单,用起来也方便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的大小就会偏大,比较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常用的。比如:
商品是图书,就不需要design、colors、sizes。
所以我们就可以把公共属性单独写出来,剩余属于各种商品本身的属性使用联合体起来,这样就可以介绍所需的内存空间,一定程度上节省了内存。
struct gift_list
{
int stock_number;// 库存量
double price; //定价
int item_type;//商品类型
union {
struct
{
char title[20];//书名
char author[20];//作者
int num_pages;//页数
}book;
struct
{
char design[30];//设计
}mug;
struct
{
char design[30];//设计
int colors;//颜⾊
int sizes;// 尺⼨
}shirt;//声明了三个匿名结构体
}item;
};
我们同样可以使用联合体来判断机器的大小端
比如最开始的代码,char类型存在首地址处,整型类型的低地址存在低位还是高位就看机器的大小端。那么我们给整型赋值1,读取char的值就好了。如果char是1,那么就是小端,如果是0,那么就是大端。
int check_sys()
{
union
{
char c;
int i;
}u//匿名联合体
u.i = 1;
return u.c;
}
int main()
{
if(check_sys() == 1)
printf("小端");
else
printf("大端");
return 0;
}