联合体和结构体是C语言中两种重要的自定义数据类型,二者在语法形式上有一定相似性,但底层的内存分配逻辑和使用场景存在本质差异,核心区别体现在内存是共享还是独立分配上。

基本定义与语法形式
结构体使用struct关键字定义,用于将多个不同类型的变量组合成一个整体,每个变量称为结构体的成员。联合体使用union关键字定义,同样可以包含多个不同类型的成员,但所有成员共享同一块内存空间。
二者的定义语法示例如下:
// 定义结构体
struct Student {
char name[20]; // 姓名
int age; // 年龄
float score; // 成绩
};
// 定义联合体
union Data {
int i; // 整型成员
float f; // 浮点型成员
char c; // 字符型成员
};
内存分配机制对比
结构体的独立内存分配
结构体的每个成员都拥有独立的内存空间,成员之间不会互相覆盖,所有成员可以同时存储有效数据。结构体的总内存大小需要考虑内存对齐规则,通常会大于等于所有成员大小之和。
以上面的struct Student为例,在64位系统中,char name[20]占20字节,int age占4字节,float score占4字节,内存对齐后总大小通常为28字节,每个成员的内存地址互不重叠。
联合体的共享内存分配
联合体的所有成员共享同一块内存空间,这块内存的大小等于联合体中最大成员的大小,同一时间只能有一个成员存储有效值,对一个成员的修改会影响其他成员的值。
以上面的union Data为例,int和float通常占4字节,char占1字节,因此联合体的总大小为4字节,三个成员的起始地址完全相同。
代码示例验证差异
通过以下代码可以直观看到二者的内存差异:
#include <stdio.h>
#include <string.h>
// 定义结构体
struct Student {
char name[20];
int age;
float score;
};
// 定义联合体
union Data {
int i;
float f;
char c;
};
int main() {
// 测试结构体
struct Student stu;
strcpy(stu.name, "Tom");
stu.age = 18;
stu.score = 90.5;
printf("结构体成员地址:n");
printf("name地址:%pn", stu.name);
printf("age地址:%pn", &stu.age);
printf("score地址:%pn", &stu.score);
printf("结构体总大小:%lu字节n", sizeof(stu));
printf("各成员值:name=%s, age=%d, score=%.1fn", stu.name, stu.age, stu.score);
// 测试联合体
union Data data;
data.i = 10;
printf("n联合体初始赋值i=10后:n");
printf("i=%d, f=%.1f, c=%dn", data.i, data.f, data.c);
printf("联合体成员地址:n");
printf("i地址:%pn", &data.i);
printf("f地址:%pn", &data.f);
printf("c地址:%pn", &data.c);
printf("联合体总大小:%lu字节n", sizeof(data));
// 修改联合体浮点成员
data.f = 3.14;
printf("n修改f=3.14后:n");
printf("i=%d, f=%.1f, c=%dn", data.i, data.f, data.c);
return 0;
}
上述代码的运行结果会显示结构体三个成员的地址各不相同,所有成员值都能正确保存;而联合体三个成员的地址完全相同,修改f的值后,i和c的值会被覆盖,无法同时保存三个成员的有效数据。
使用场景差异
结构体适合需要同时存储多个不同类型相关数据的场景,比如存储学生的完整信息、商品的多维度属性等,所有数据需要长期共存、互不干扰。
联合体适合同一块内存需要在不同时刻存储不同类型数据的场景,比如通信协议中同一段数据可能是整型也可能是浮点型,或者需要节省内存空间时,用联合体可以复用同一块内存,减少内存占用。
核心差异总结
| 对比维度 | 结构体(struct) | 联合体(union) |
|---|---|---|
| 内存分配 | 每个成员独立内存 | 所有成员共享同一块内存 |
| 内存大小 | 大于等于所有成员大小之和(考虑对齐) | 等于最大成员的大小 |
| 数据存储 | 所有成员可同时存有效值 | 同一时间仅一个成员存有效值 |
| 成员修改影响 | 修改一个成员不影响其他成员 | 修改一个成员会覆盖其他成员的值 |
| 适用场景 | 多属性数据共存 | 同一内存复用存储不同类型数据 |