C 语言联合体
内容简介
union 让多个成员共享同一块存储空间。它适合表达“同一段内存,在不同情况下保存不同类型的数据”,常见于解释器值对象、协议解析、硬件接口、节省内存的嵌入式数据结构。
联合体不是“结构体的省内存版本”。它的核心语义是:多个成员重叠,同一时刻通常只有一个成员是当前有效含义。
用法
基本定义
typedef union Number {
int i;
float f;
} Number;
Number n;
n.i = 42;联合体大小至少能容纳最大成员,并满足最严格成员的对齐要求。
成员共享存储
Number n;
n.i = 42;
n.f = 3.14f; // 写入 f 后,i 不再代表原来的有效含义写入一个成员后,再按另一个不兼容成员读取,通常不应该作为可移植写法。
带标签联合体
直接使用 union 容易忘记当前有效成员,工程中常配合枚举保存类型标签。
typedef enum ValueKind {
VALUE_INT,
VALUE_DOUBLE
} ValueKind;
typedef struct Value {
ValueKind kind;
union {
int i;
double d;
} data;
} Value;读取时先判断标签:
void value_print(const Value *value)
{
if (value == NULL) {
return;
}
switch (value->kind) {
case VALUE_INT:
printf("%d\n", value->data.i);
break;
case VALUE_DOUBLE:
printf("%f\n", value->data.d);
break;
}
}匿名联合体成员
C11 支持匿名联合体成员,常用于减少一层 .data 访问。
typedef struct Token {
int kind;
union {
int int_value;
double double_value;
};
} Token;
Token token;
token.int_value = 10;注意项目编译标准和编译器支持。
最佳代码实践
- 联合体优先配合枚举标签使用,明确当前有效成员。
- 对外接口不要让调用者猜联合体当前保存什么。
- 如果联合体成员包含资源指针,要明确初始化、销毁和所有权规则。
- 不要把联合体当成通用类型转换工具;类型转换优先用清楚的转换函数。
- 用于协议或文件格式时,优先显式解析字节,不要直接依赖联合体布局。
错误用法
typedef union Number {
int i;
float f;
} Number;
Number n;
n.i = 42;
printf("%f\n", n.f); // 错误倾向:按另一个成员解释不可靠typedef struct Value {
int kind;
union {
int i;
char *s;
} data;
} Value;
Value value;
printf("%s\n", value.data.s); // 错误:没有确认当前有效成员和指针有效性注意事项
- 联合体所有成员地址通常相同,但类型含义不同。
- 联合体的大小不是所有成员大小之和,而是至少容纳最大成员。
- 联合体可用于节省内存,但会增加“当前到底是什么类型”的维护成本。
- 在 C++ 中,联合体成员如果有非平凡构造/析构会引入更多生命周期规则;这已超出 C 主线。