在C语言中,联合体(Union)和结构体(Struct)从语法形式上看非常相似,但它们的实际用途和内存分配机制却完全不同。如果稍不注意,就很容易引发程序的致命错误。

1. 先理解结构体(Struct)
结构体是一种将不同类型的数据组合在一起的数据结构,每个成员都拥有独立的内存空间。
#include <iostream>
struct DataStruct {
int i;
float f;
char str[20];
};
假设我们有这样一个结构体 DataStruct,它包含三个成员变量:
- int i —— 占用4个字节
- float f —— 占用4个字节
- char str[20] —— 占用20个字节
在忽略内存对齐的情况下,结构体的大小是所有成员大小的总和,即 4 + 4 + 20 = 28个字节。
我们可以通过代码来验证:
int size1 = sizeof(DataStruct);
std::cout << "size of DataStruct: " << size1 << std::endl;
同时,我们还可以查看每个成员变量的内存地址:
DataStruct dataStruct;
char* i_addr = (char*)&dataStruct.i;
char* f_addr = (char*)&dataStruct.f;
char* str_addr = (char*)&dataStruct.str;
printf("i_addr: %p\n", i_addr);
printf("f_addr: %p\n", f_addr);
printf("str_addr: %p\n", str_addr);
运行结果会显示:f变量的地址刚好在i变量地址之后4个字节,str变量的地址又在f变量之后4个字节。这说明结构体中的成员在内存中是依次连续排列的。
2. 联合体(Union)的内存机制
现在我们来看联合体:
union DataUnion {
int i;
float f;
char str[20];
};
同样使用上述三个成员,但当我们用 sizeof(DataUnion) 计算其大小时,却发现它只占用 20个字节——也就是最大成员 char str[20] 所占用的空间。
联合体的关键特性是:
- 所有成员共用同一块内存
- 联合体的大小由其最大的成员决定
- 同一时间只能有效存储一个成员的值
3. 联合体成员地址与值覆盖演示
我们来看下面的代码:
#include <iostream>
union DataUnion {
int i;
float f;
char str[20];
};
int main() {
int size1 = sizeof(DataUnion);
std::cout << "size of DataUnion: " << size1 << std::endl;
DataUnion dataUnion = { 0 };
std::cout << "i= " << dataUnion.i << std::endl;
std::cout << "f= " << dataUnion.f << std::endl;
dataUnion.i = 10;
std::cout << "f= " << dataUnion.f << std::endl;
dataUnion.f = 20;
std::cout << "i= " << dataUnion.i << std::endl;
return 1;
}
运行后你会发现:
- 三个成员变量的内存地址是完全相同的
- 当修改 i 的值后,f 的值会发生变化
- 当修改 f 的值后,i 的值也会随之改变
这是因为它们共享同一块内存,只是以不同的数据类型去解读这块内存中的比特位。
4. 联合体的实际应用场景
联合体的内存共享特性使其在以下场景中非常有用:
- 需要存储多种数据类型,但在同一时间只需要使用其中一种时
- 解析网络数据包或硬件寄存器值(不同方式解读同一内存)
- 节省内存空间(尤其在嵌入式系统中)
总结
结构体(Struct):成员各自拥有独立内存,适合把相关数据打包在一起。
联合体(Union):所有成员共享同一块内存,大小由最大成员决定,适合多种类型互斥使用的场景。
理解两者在内存布局上的根本区别,是正确使用联合体的关键。使用时一定要清楚当前正在操作的是哪一个成员,否则很容易引发难以调试的内存错误。


