什么是 union?
union 是一种特殊的自定义数据类型,它允许你在相同的内存位置存储不同类型的数据,与 struct(结构体)不同,struct 会为每个成员分配独立的内存空间,而 union 的所有成员共享同一块内存。

union 的大小等于其最大成员的大小,在任何时刻,union 中只存放一个成员的值,对其中一个成员的赋值会覆盖其他成员的值。
一个简单的 union 示例:
#include <stdio.h>
// 定义一个 union
DataUnion {
int i;
float f;
char c;
};
int main() {
DataUnion data;
data.i = 10;
printf("data.i = %d\n", data.i); // 输出: data.i = 10
printf("data.f = %f\n", data.f); // 输出: data.f = 0.000000 (值被覆盖,无意义)
printf("data.c = %c\n", data.c); // 输出: data.c = (值被覆盖,无意义)
printf("--------------------\n");
data.f = 3.14f;
printf("data.i = %d\n", data.i); // 输出: data.i = 1078530011 (值被覆盖,无意义)
printf("data.f = %f\n", data.f); // 输出: data.f = 3.140000
printf("data.c = %c\n", data.c); // 输出: data.c = (值被覆盖,无意义)
return 0;
}
union 的初始化
union 的初始化规则与 struct 类似,但有几个关键点需要注意。
1 C89/C90 标准
在较老的 C89/C90 标准中,union 的初始化方式非常受限:你只能使用其第一个成员的值进行初始化。

语法格式:
union tag_name variable_name = { initial_value_for_first_member };
示例:
#include <stdio.h>
DataUnion data = { 100 }; // 合法:使用第一个成员 int i 的值进行初始化
// DataUnion data2 = { 3.14f }; // 非法:不能使用第二个成员 float f 的值初始化
// DataUnion data3 = { 'A' }; // 非法:不能使用第三个成员 char c 的值初始化
为什么会有这个限制?
因为编译器需要知道如何将初始值存入 union 的内存中,由于所有成员共享内存,从第一个成员开始初始化是最直接、最明确的方式。
2 C99 及以后标准
从 C99 标准开始,union 的初始化规则得到了极大的放宽,你可以使用花括号 列表来初始化 union 的任意成员。

语法格式:
// 方式一:使用指定成员初始化 (推荐)
union tag_name variable_name = {
.member_name = initial_value
};
// 方式二:使用成员顺序初始化 (类似于结构体)
union tag_name variable_name = { initial_value_for_any_member };
示例:
#include <stdio.h>
DataUnion data1 = { .f = 3.14f }; // 合法:使用指定成员 float f 进行初始化
DataUnion data2 = { .c = 'X' }; // 合法:使用指定成员 char c 进行初始化
DataUnion data3 = { 200 }; // 合法:C99 允许,等同于初始化第一个成员 int i
使用指定成员初始化(.member_name)的优点:
- 可读性强:代码清晰地表明了你想初始化哪个成员。
- 安全性高:即使你重新定义
union,调整了成员的顺序,这段初始化代码依然能正确工作。 - 可初始化任意成员:这是最大的好处,不再局限于第一个成员。
代码示例:C99 标准下的初始化
下面是一个完整的、对比不同初始化方式的示例。
#include <stdio.h>
// 定义一个 union
DataUnion {
int i;
float f;
char c;
};
int main() {
// --- 1. 使用指定成员初始化 (C99 及以后) ---
printf("--- 使用指定成员初始化 ---\n");
DataUnion data1 = { .i = 42 }; // 初始化 int 成员
printf("data1.i = %d\n", data1.i);
printf("data1.f = %f (无意义)\n", data1.f);
printf("data1.c = %c (无意义)\n\n", data1.c);
DataUnion data2 = { .f = 2.71828f }; // 初始化 float 成员
printf("data2.f = %f\n", data2.f);
printf("data2.i = %d (无意义)\n", data2.i);
printf("data2.c = %c (无意义)\n\n", data2.c);
DataUnion data3 = { .c = 'Z' }; // 初始化 char 成员
printf("data3.c = %c\n", data3.c);
printf("data3.i = %d (无意义)\n", data3.i);
printf("data3.f = %f (无意义)\n\n", data3.f);
// --- 2. 使用成员顺序初始化 (C99 及以后) ---
// 这等同于初始化第一个成员
printf("--- 使用成员顺序初始化 ---\n");
DataUnion data4 = { 999 }; // 初始化第一个成员 int i
printf("data4.i = %d\n", data4.i);
printf("data4.f = %f (无意义)\n", data4.f);
printf("data4.c = %c (无意义)\n\n");
// --- 3. 不使用初始化列表(默认初始化) ---
// 注意:union 的默认初始化是未定义行为
// 不同编译器可能有不同表现,不要依赖它
printf("--- 默认初始化 (不推荐) ---\n");
DataUnion data5;
// data5 的值是未定义的,直接使用是危险的
// printf("data5.i = %d\n", data5.i); // 可能打印出垃圾值
printf("请勿依赖默认初始化,\n");
return 0;
}
重要注意事项
-
访问被覆盖的成员是危险的 当你向
union的一个成员写入数据后,再读取另一个成员的值,得到的是该内存被解释为另一种类型后的结果,这通常不是你想要的,甚至可能导致程序崩溃。 -
初始化列表长度 你不能提供超过一个成员的初始值,编译器会报错。
// 错误示例 DataUnion data_error = { .i = 10, .f = 3.14 }; // 错误:太多初始化项 -
默认初始化 如果像声明普通变量一样声明
union而不进行初始化(如DataUnion my_data;),它的内容是未定义的,你不能假设它的成员是零,这是与struct不同的地方(全局/静态的struct默认初始化为零,但union不是)。
| 标准 | 初始化方式 | 说明 |
|---|---|---|
| C89/C90 | union U u = { value }; |
只能用第一个成员的值进行初始化。 |
| C99 及以后 | union U u = { .member = value }; |
推荐,使用指定成员进行初始化,灵活且安全。 |
| C99 及以后 | union U u = { value }; |
允许,效果等同于初始化第一个成员。 |
| 任何标准 | union U u; |
默认初始化是未定义的,不要依赖。 |
最佳实践:
在现代 C 编程(遵循 C99 或更新标准)中,强烈推荐使用指定成员的初始化方式(.member_name = value),这种方式最清晰、最安全,也最能体现 union 的意图。
