C语言共用体(union)知识详解
专为编程小白准备的通俗易懂的共用体知识总结
什么是共用体?
共用体(union)是C语言中一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。
核心特点:
- 所有成员共享同一块内存空间
- 任何时候只能有一个成员包含有效值
- 内存大小由最大成员决定
- 节省内存空间,但需要小心使用
定义方式:
union 共用体名称 {
数据类型 成员1;
数据类型 成员2;
// 更多成员…
} 变量名;
数据类型 成员1;
数据类型 成员2;
// 更多成员…
} 变量名;
共用体与结构体的区别
结构体(struct)
- 每个成员都有自己的内存空间
- 所有成员可以同时存储值
- 内存大小 = 所有成员大小之和 + 内存对齐调整
- 适合存储多个相关数据项
共用体(union)
- 所有成员共享同一内存空间
- 一次只能使用一个成员
- 内存大小 = 最大成员的大小
- 适合节省内存或多种类型数据复用
内存对比示例:
struct MyStruct {
int a; // 4字节
float b; // 4字节
char c; // 1字节
}; // 总大小 ≈ 12字节 (考虑内存对齐)
union MyUnion {
int a; // 4字节
float b; // 4字节
char c; // 1字节
}; // 总大小 = 4字节 (最大成员的大小)
int a; // 4字节
float b; // 4字节
char c; // 1字节
}; // 总大小 ≈ 12字节 (考虑内存对齐)
union MyUnion {
int a; // 4字节
float b; // 4字节
char c; // 1字节
}; // 总大小 = 4字节 (最大成员的大小)
共用体使用场景
1. 节省内存空间
当多个数据不会同时使用时,共用体可以大幅节省内存。
2. 实现多类型变量
需要存储不同类型数据但一次只用一种类型时。
3. 硬件寄存器访问
在嵌入式编程中,同一内存位置可能对应不同功能的寄存器。
4. 协议解析
解析网络数据包时,同一字段可能有不同的解释方式。
5. 类型转换技巧
在不进行强制类型转换的情况下查看变量在不同类型下的表示。
// 示例:使用共用体提取浮点数的字节表示
union FloatExtractor {
float f_value;
unsigned char bytes[4];
};
union FloatExtractor fe;
fe.f_value = 3.14159f;
printf(“浮点数的字节表示: “);
for(int i = 0; i < 4; i++) {
printf(“%02X “, fe.bytes[i]);
}
union FloatExtractor {
float f_value;
unsigned char bytes[4];
};
union FloatExtractor fe;
fe.f_value = 3.14159f;
printf(“浮点数的字节表示: “);
for(int i = 0; i < 4; i++) {
printf(“%02X “, fe.bytes[i]);
}
内存布局可视化
结构体(struct)内存布局
int成员 (4字节)
0x0000 – 0x0003
float成员 (4字节)
0x0004 – 0x0007
char成员 (1字节)
0x0008
结构体总大小: 12字节 (考虑对齐)
共用体(union)内存布局
int成员 (4字节)
所有成员共享
同一内存区域
同一内存区域
float成员 (4字节)
所有成员共享
同一内存区域
同一内存区域
char成员 (1字节)
所有成员共享
同一内存区域
同一内存区域
共用体总大小: 4字节 (最大成员的大小)
使用注意事项
1. 成员覆盖问题
给一个成员赋值会覆盖其他成员的值,因为所有成员共享同一内存。
2. 类型安全
C语言不会检查你访问的是哪个成员,需要程序员自己记录当前使用的是哪个成员。
3. 初始化
只能初始化共用体的第一个成员。
4. 大小端问题
在不同系统中,字节顺序可能不同,会影响共用体中多字节数据的解释。
// 错误使用示例:访问被覆盖的成员
union Data {
int i;
float f;
};
union Data data;
data.i = 10; // 设置整数值
printf(“%d”, data.i); // 正确:输出10
data.f = 3.14; // 设置浮点值
printf(“%d”, data.i); // 错误!i的值已被覆盖
union Data {
int i;
float f;
};
union Data data;
data.i = 10; // 设置整数值
printf(“%d”, data.i); // 正确:输出10
data.f = 3.14; // 设置浮点值
printf(“%d”, data.i); // 错误!i的值已被覆盖
完整示例代码
#include <stdio.h>
#include <string.h>
// 定义一个共用体
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
printf(“共用体大小: %ld 字节\n\n”, sizeof(data));
// 使用整形成员
data.i = 42;
printf(“data.i 设置: %d\n”, data.i);
printf(“尝试访问 data.f: %f (无意义)\n\n”, data.f);
// 使用浮点成员 – 覆盖之前的整数
data.f = 3.14159;
printf(“data.f 设置: %f\n”, data.f);
printf(“尝试访问 data.i: %d (无意义)\n\n”, data.i);
// 使用字符串成员 – 覆盖之前的浮点数
strcpy(data.str, “Hello, Union!”);
printf(“data.str 设置: %s\n”, data.str);
printf(“尝试访问 data.f: %f (无意义)\n\n”, data.f);
// 正确用法:使用标签记录当前存储的类型
struct TaggedData {
int type; // 0=int, 1=float, 2=string
union {
int i;
float f;
char str[20];
} data;
};
struct TaggedData tagged;
tagged.type = 0; // 存储整数
tagged.data.i = 100;
// 使用时检查类型
if(tagged.type == 0) {
printf(“\n带标签的共用体: int值 = %d\n”, tagged.data.i);
}
return 0;
}
#include <string.h>
// 定义一个共用体
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
printf(“共用体大小: %ld 字节\n\n”, sizeof(data));
// 使用整形成员
data.i = 42;
printf(“data.i 设置: %d\n”, data.i);
printf(“尝试访问 data.f: %f (无意义)\n\n”, data.f);
// 使用浮点成员 – 覆盖之前的整数
data.f = 3.14159;
printf(“data.f 设置: %f\n”, data.f);
printf(“尝试访问 data.i: %d (无意义)\n\n”, data.i);
// 使用字符串成员 – 覆盖之前的浮点数
strcpy(data.str, “Hello, Union!”);
printf(“data.str 设置: %s\n”, data.str);
printf(“尝试访问 data.f: %f (无意义)\n\n”, data.f);
// 正确用法:使用标签记录当前存储的类型
struct TaggedData {
int type; // 0=int, 1=float, 2=string
union {
int i;
float f;
char str[20];
} data;
};
struct TaggedData tagged;
tagged.type = 0; // 存储整数
tagged.data.i = 100;
// 使用时检查类型
if(tagged.type == 0) {
printf(“\n带标签的共用体: int值 = %d\n”, tagged.data.i);
}
return 0;
}
© 2023 C语言共用体知识总结 | 帮助编程小白理解共用体的核心概念
提示:在实际编程中,请确保正确使用共用体,避免访问无效数据!