共用体(Union)和结构体(Struct)都是C语言中用于定义复合数据类型的关键工具,但它们在数据存储和管理内存的方式上有重要区别。
结构体(Struct)
- 内存布局:在结构体中,每个成员都有自己的内存空间,结构体的总大小至少是所有成员大小的总和(可能更大,因为内存对齐)。
- 成员访问:结构体的每个成员可以同时拥有各自的值。更改一个成员的值不会影响其他成员。
- 用途:结构体通常用于将相关的数据项组合在一起,例如表示一个对象的不同属性。
struct Person {
char name[50];
int age;
float height;
};
共用体(Union)
- 内存布局:共用体中的所有成员共享同一块内存空间,共用体的大小等于其最大成员的大小。
- 成员访问:一次只能有效地使用共用体的一个成员。赋值给某个成员会覆盖其他成员的值,因为它们共享内存。
- 用途:共用体常用于存储可能以多种形式存在的数据,或用于节省内存,尤其是当不同的成员不会同时使用时。
union Data {
int i;
float f;
char str[20];
};
关键区别
-
内存使用:结构体为每个成员分配单独的内存,而共用体使所有成员共享内存。
-
成员独立性:结构体的成员是独立的,共用体的成员则不是。
-
大小:结构体的大小至少等于其所有成员大小之和,共用体的大小等于其最大成员的大小。
结构体举例:员工信息系统
假设开发者在创建一个员工信息系统。在这个系统中,每个员工有姓名、年龄和工号等属性。这些属性可以使用结构体来表示,因为每个员工的这些信息都是相互独立且同时存在的。
#include <stdio.h>
// 定义员工结构体
struct Employee {
char name[50];
int age;
int employeeId;
};
int main() {
// 创建一个Employee结构体变量
struct Employee emp1;
// 设置emp1的信息
strcpy(emp1.name, "Alice");
emp1.age = 30;
emp1.employeeId = 1001;
// 打印emp1的信息
printf("Employee Name: %s\n", emp1.name);
printf("Age: %d\n", emp1.age);
printf("Employee ID: %d\n", emp1.employeeId);
return 0;
}
共用体举例:数据解析
假设你正在编写一个程序来解析不同类型的数据包。每个数据包的第一个字节是一个类型标识符,它决定了包的剩余部分应该如何解释。这种情况下,可以使用共用体,因为数据包的不同部分(虽然都存在),但在任何给定时间只使用其中的一个。
#include <stdio.h>
// 定义数据包共用体
union DataPacket {
int intValue;
float floatValue;
char strValue[20];
};
int main() {
// 创建一个DataPacket共用体变量
union DataPacket packet;
// 假设我们接收了一个整数数据
packet.intValue = 123;
// 由于我们知道当前数据类型是整数,只打印intValue
printf("Integer Value: %d\n", packet.intValue);
// 之后,假设我们接收了一个字符串数据
strcpy(packet.strValue, "Hello World");
// 现在只使用strValue
printf("String Value: %s\n", packet.strValue);
// 注意,此时intValue的内容已经被覆盖
return 0;
}
注意,此时intValue的内容已经被覆盖
在共用体(Union)中,所有成员共享同一块内存区域。这意味着当你更改共用体中的一个成员时,其他成员所占用的内存内容也会被更改。在上面的例子中,这个特性体现在对共用体 DataPacket 成员的赋值操作上。
首先,给 intValue 成员赋值:
packet.intValue = 123;
然后,给 strValue 成员赋值:
strcpy(packet.strValue, "Hello World");
在这个赋值操作中,共用体 packet 的内存被用来存储字符串 “Hello World”。由于 strValue 和 intValue 共享相同的内存区域,原先存储的整数 123 的二进制表示被 “Hello World” 的内容覆盖了。
当你再次尝试访问 packet.intValue 时,得到的不再是原先存储的整数 123,而是内存中当前存储的 “Hello World” 字符串对应部分的二进制数据,解释为整数的形式。这通常会是一个无意义的数值,因为字符串的二进制表示与整数的二进制表示完全不同。
所以注意,此时intValue 的内容已经被覆盖是指:当 strValue 被赋予新值时,之前存储在 intValue 中的数据被新数据覆盖了。这正是共用体的核心特性,即所有成员共享同一块内存,因此任何时候只能安全地使用其中一个成员。