- C++
Union(共用体)教程
- 2025-8-29 14:26:33 @
Union(共用体)教程
在 C/C++ 中,union
(共用体)是一种特殊的数据结构,其核心特点是所有成员共享同一块内存空间,而非像结构体(struct
)那样为每个成员分配独立内存。这意味着共用体的大小由其占用内存最大的成员决定,且修改一个成员会覆盖其他成员的值。
一、基本定义与语法
共用体通过 union
关键字声明,语法与结构体类似,成员间用逗号分隔,末尾需加分号。
语法格式
union 共用体名 {
数据类型1 成员名1;
数据类型2 成员名2;
// ... 其他成员
};
示例:定义并使用共用体
#include <stdio.h>
// 定义共用体,成员共享内存
union Data {
int i; // 4字节(通常)
float f; // 4字节(通常)
char c; // 1字节
};
int main() {
union Data data;
// 1. 查看共用体大小(由最大成员决定,此处为4字节)
printf("共用体大小:%zu 字节\n", sizeof(data)); // 输出:4 字节
// 2. 赋值并访问成员(修改一个成员会覆盖其他成员)
data.i = 0x12345678; // 给int成员赋值(十六进制)
printf("data.i = %#x\n", data.i); // 输出:0x12345678
printf("data.c = %#x\n", data.c); // 输出:0x78(仅取内存低1字节)
data.f = 3.14f; // 给float成员赋值,覆盖int成员的内存
printf("data.f = %.2f\n", data.f); // 输出:3.14
printf("data.i = %#x\n", data.i); // 输出:随机值(内存已被float覆盖)
return 0;
}
二、核心特性:内存共享
-
内存共用:所有成员的首地址相同,共用同一块内存,修改一个成员会直接覆盖其他成员的内存数据(因为内存被复用)。
例:上述代码中,data.i
赋值后,data.c
只能读取data.i
内存的低1字节;赋值data.f
后,data.i
的值因内存被覆盖而失效。 -
大小由最大成员决定:共用体的总内存大小 = 其最大成员的内存大小(需考虑内存对齐,与结构体对齐规则一致)。
例:若共用体包含char
(1字节)和double
(8字节),则共用体大小为 8 字节。 -
同一时间只能用一个成员:因内存共享,同一时刻只能安全访问“最近赋值的成员”,访问未赋值的成员会得到无意义的垃圾值。
三、常见使用场景
共用体的核心价值是节省内存,适用于“同一内存区域在不同场景下存储不同类型数据”的场景:
1. 节省内存(内存受限场景)
当数据不会同时使用时,用共用体替代结构体,减少内存占用。
例:存储“学生信息”,学生要么填“学号(int)”,要么填“临时编号(char[10])”,二者不同时使用:
union ID {
int student_id; // 学号(4字节)
char temp_id[10]; // 临时编号(10字节)
};
struct Student {
char name[20];
union ID id; // 共用体节省内存,避免同时存储两种编号
};
2. 访问同一内存的不同类型
通过不同类型的成员,解析同一块内存的二进制数据(常用于底层数据处理,如硬件寄存器、数据格式转换)。
例:将 int
类型的4字节内存,按字节拆分读取:
union IntByte {
int i;
char bytes[4]; // 用char数组拆分int的4个字节
};
int main() {
union IntByte ib;
ib.i = 0x11223344;
// 按字节输出int的4个部分(小端机器下,低字节存低地址)
for (int j = 0; j < 4; j++) {
printf("字节%d:%#x\n", j, ib.bytes[j]); // 输出:0x44、0x33、0x22、0x11
}
return 0;
}
3. 与结构体结合实现“变体类型”
通过“结构体+共用体”,实现类似“多类型数据”的存储(如C++的variant
简化版)。
例:存储“不同类型的消息”,消息要么是int
,要么是char*
:
// 标记消息类型
enum MsgType { INT_MSG, STR_MSG };
struct Message {
enum MsgType type; // 标记当前共用体使用的成员
union Content { // 消息内容,二选一
int num;
char* str;
} content;
};
// 使用时通过type判断成员类型,避免访问错误
void printMsg(struct Message msg) {
if (msg.type == INT_MSG) {
printf("整数消息:%d\n", msg.content.num);
} else if (msg.type == STR_MSG) {
printf("字符串消息:%s\n", msg.content.str);
}
}
四、与结构体(struct)的区别
对比维度 | union(共用体) | struct(结构体) |
---|---|---|
内存分配 | 所有成员共享同一块内存 | 为每个成员分配独立内存,总大小=成员大小之和(含对齐) |
大小计算 | 大小 = 最大成员的大小(含对齐) | 大小 = 各成员大小之和(含对齐) |
成员访问 | 同一时间只能安全访问一个成员 | 可同时访问所有成员 |
数据覆盖 | 修改一个成员会覆盖其他成员 | 修改一个成员不影响其他成员 |
核心用途 | 节省内存、解析同一内存的不同类型 | 存储多个相关且需同时存在的数据 |
五、注意事项
- 避免同时访问多个成员:因内存共享,未赋值的成员值是垃圾值,访问会导致逻辑错误。
- 成员的初始化:共用体只能初始化第一个成员(C语言),或通过指定成员初始化(C++11及以后)。
例:union Data d = {10};
(初始化第一个成员i
);C++中可写union Data d = {.f = 3.14f};
。 - 不支持引用成员:C/C++中,共用体的成员不能是引用(引用必须绑定唯一对象,与内存共享冲突)。
- 内存对齐:共用体的对齐规则与结构体一致,需注意成员类型的对齐要求(如
char
和double
组成的共用体,会按double
的8字节对齐)。 - C++中的扩展:C++中,共用体可包含有构造函数/析构函数的成员(需手动管理生命周期),但C语言不支持。
六、总结
union
的核心是内存共享,目的是节省内存,适用于“同一内存区域存储不同类型数据”的场景。- 使用时需通过“类型标记”(如枚举)明确当前访问的成员,避免数据覆盖导致的错误。
- 与结构体的本质区别是“内存是否共享”,需根据数据是否“同时存在”选择使用。