在 C 语言中,union
(联合体) 是一种数据结构,它允许多个成员共享相同的内存空间。换句话说,联合体中的所有成员都存储在同一块内存区域,不同的成员会占用相同的内存地址,但在同一时刻只能保存一个成员的值。
1. union
的定义和基本结构
定义 union
的语法与 struct
类似,但在 union
中,所有成员共享同一块内存区域,而在 struct
中,每个成员都有自己独立的内存空间。
c
union Data {
int i;
float f;
char str[20];
};
这个 union Data
包含三个成员:i
(整型)、f
(浮点型)、str
(字符数组)。在内存中,i
、f
和 str
是共享同一块存储空间的,因此它们不能同时保存值。
2. union
的内存分配
在 union
中,分配的内存大小等于最大成员的大小。例如:
c
union Data {
int i; // 占 4 字节
float f; // 占 4 字节
char str[20]; // 占 20 字节
};
- 联合体
Data
的大小是 20 字节(str[20]
是最大的成员),所有成员都共享这 20 字节的内存。
3. 如何使用 union
你可以像结构体一样定义和访问 union
的成员。注意,一次只能使用一个成员,并且最后存入的值会覆盖之前的值。
c
#include <stdio.h>
#include <string.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 10;
printf("data.i : %d\n", data.i);
data.f = 220.5;
printf("data.f : %f\n", data.f);
strcpy(data.str, "Hello");
printf("data.str : %s\n", data.str);
return 0;
}
在这段代码中,data.i
最先被赋值为 10
,然后 data.f
被赋值为 220.5
,最后 data.str
被赋值为 "Hello"
。注意,当 str
被赋值后,之前的 i
和 f
的值都被覆盖。
4. union
的用途
union
通常用于节省内存,尤其在硬件编程或底层系统开发中,常用于处理以下场景:
4.1 节省内存
如果你有多个数据类型,但这些类型不会同时使用,可以通过 union
节省内存。例如在嵌入式系统中,数据存储空间非常有限,你可以使用 union
存储多种不同的类型。
4.2 数据类型的重解释
union
允许你以不同的方式解释相同的内存。这在需要对数据进行不同类型的访问时非常有用。
例如,当你想通过字节数组访问一个整数的字节内容时,可以使用 union
:
c
union IntByte {
int i;
unsigned char bytes[4];
};
int main() {
union IntByte data;
data.i = 0x12345678;
printf("Bytes: %x %x %x %x\n", data.bytes[0], data.bytes[1], data.bytes[2], data.bytes[3]);
return 0;
}
这段代码允许你查看整数 0x12345678
在内存中的字节表示。
4.3 硬件寄存器访问
在嵌入式系统中,经常需要访问硬件寄存器,寄存器通常可以用 union
来表示。硬件寄存器有时既可以按位访问,也可以按字节或整块访问,使用 union
可以同时处理这些不同的访问方式。
例如:
c
union Register {
uint32_t full;
struct {
uint8_t byte1;
uint8_t byte2;
uint8_t byte3;
uint8_t byte4;
};
};
这段代码允许你通过 full
访问整个 32 位寄存器,也可以分别访问每个 8 位字节。
4.4 变体数据结构
在一些协议栈或者文件格式中,你可能需要存储多种类型的数据,但在一次操作中只使用其中一种。例如,一个网络协议包可能包含多个不同类型的字段,你可以使用 union
来表示这些字段,从而在解析时节省内存。
5. union
的局限性
- 同时只能存储一个值 :联合体中的所有成员共享相同的内存空间,因此在任一时刻只能存储一个成员的值。如果你想同时使用多个值,就需要考虑使用
struct
。 - 调试复杂性:由于不同的成员共享同一片内存,因此调试时可能不容易区分哪个成员当前有效。
6. 总结
union
是一种允许多个成员共享同一块内存的结构体,常用于节省内存或需要以不同方式解释相同内存时。它在嵌入式系统、硬件编程和数据解析方面非常有用,但要注意它一次只能保存一个有效的值,并且在使用中需要小心,以防止未定义的行为。