联合体
联合体是一种特殊的派生类型,它允许在相同的内存位置存储不同类型的数据。与结构体不同,联合体的所有成员共享同一块内存空间,在任何时刻只能存储其中一个成员的值。
1. 定义
关键字 union
+ "名字" + 声明列表 其中声明列表是一个用逗号分隔,花括号环绕的列表,列表中的每一项是一个成员的声明
c
union data {
int i;
float f;
char c;
};
1
2
3
4
5
2
3
4
5
上述代码定义了一个联合体类型 union data
,它包含三个成员:i
、f
和 c
。但它们共享同一块内存,大小等于最大成员的大小(在这里是 int
或 float
的大小,通常为 4 字节)。
之后声明联合体对象时:
c
union data a; // 声明一个 union data 类型的对象 a
1
typedef
起别名
c
typedef union {
int i;
float f;
char c;
} data;
1
2
3
4
5
2
3
4
5
或者
c
union data {
int i;
float f;
char c;
};
typedef union data data;
1
2
3
4
5
6
2
3
4
5
6
取别名之后,可以用 data
表示这个联合体类型(在第二种情况中,union data
也可用):
c
data a; // 声明一个 union data 类型的对象 a
1
2. 初始化
联合体的初始化只能使用第一个成员的类型,或者使用指派符指定特定成员:
c
union data d1 = { 42 }; // 初始化第一个成员 i
union data d2 = { .f = 3.14f }; // 使用指派符初始化成员 f
union data d3 = { .c = 'A' }; // 使用指派符初始化成员 c
1
2
3
2
3
注意
联合体在同一时间只能存储一个成员的值。当你给某个成员赋值时,其他成员的值就变得不确定了。
3. 访问成员
联合体的成员访问方式与结构体相同,使用点运算符 .
:
c
#include <stdio.h>
union data {
int i;
float f;
char c;
};
int main(void) {
union data d;
d.i = 42;
printf("d.i = %d\n", d.i); // 输出:42
d.f = 3.14f;
printf("d.f = %f\n", d.f); // 输出:3.14
// printf("d.i = %d\n", d.i); // 输出:未定义行为,因为被 f 覆盖了
d.c = 'A';
printf("d.c = %c\n", d.c); // 输出:A
// printf("d.f = %f\n", d.f); // 输出:未定义行为,因为被 c 覆盖了
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
4. 常见用途
4.1“节省内存”
当你需要存储不同类型的数据,但在任何时刻只需要其中一种时:
c
union number {
int as_int;
float as_float;
double as_double;
};
// 只占用 double 的大小(8 字节),而不是三者之和
1
2
3
4
5
6
7
2
3
4
5
6
7
4.2 类型转换和位操作
联合体可以用来查看同一数据的不同表示:
c
#include <stdio.h>
union converter {
float f;
unsigned int bits;
};
int main(void) {
union converter conv;
conv.f = 3.14f;
printf("浮点数:%f\n", conv.f);
printf("位表示:0x%08x\n", conv.bits);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4.3 带标签的联合体(Tagged Union)
结合枚举类型,可以创建安全的联合体:
c
typedef enum {
TYPE_INT,
TYPE_FLOAT,
TYPE_STRING
} value_type;
typedef struct {
value_type type;
union {
int i;
float f;
char* s;
} data;
} tagged_value;
void print_value(tagged_value val) {
switch (val.type) {
case TYPE_INT:
printf("整数:%d\n", val.data.i);
break;
case TYPE_FLOAT:
printf("浮点数:%f\n", val.data.f);
break;
case TYPE_STRING:
printf("字符串:%s\n", val.data.s);
break;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
5. 初始化
联合体只能用第一个成员的类型进行初始化(除非使用指派符)。
c
union example {
int i;
float f;
};
union example e1 = { 42 }; // 正确:初始化第一个成员
union example e2 = { 3.14f }; // 错误:类型不匹配
union example e3 = { .f = 3.14f }; // 正确:使用指派符
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
习题
[1.1] 编写程序,使用联合体实现一个简单的类型转换器,能够查看整数的浮点数表示,以及浮点数的二进制位表示。
[1.3] 实现一个通用的类型,使用带标签的联合体存储整数、浮点数或字符串,并提供相应的打印和比较函数。
[2.1] 设计一个表达式求值器,使用联合体表示不同类型的表达式节点(数字、运算符等),实现简单的四则运算。
[1.2] 编写程序演示联合体的内存布局,计算并输出不同联合体类型的大小,验证内存对齐的影响。