struct (结构体)
struct 是 C 语言中用于将多个不同或相同类型的变量组合成一个单一的逻辑单元的机制,你可以把它想象成一个“容器”或者一个“数据包”,里面可以存放各种东西。

(图片来源网络,侵删)
核心思想
将相关的数据项捆绑在一起,形成一个更复杂、更有意义的数据类型,这有助于组织和管理复杂数据,使代码更具可读性和可维护性。
语法
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
// ...
数据类型 成员n;
};
// 声明结构体变量
struct 结构体名 变量名;
示例:学生信息
假设我们要存储一个学生的信息,包括学号、姓名、年龄和成绩,这些数据类型各不相同,但都属于“学生”这个概念。
#include <stdio.h>
#include <string.h>
// 1. 定义一个名为 Student 的结构体
struct Student {
int id; // 学号
char name[50]; // 姓名
int age; // 年龄
float score; // 成绩
};
int main() {
// 2. 声明一个结构体变量 s1
struct Student s1;
// 3. 访问和赋值成员
s1.id = 1001;
strcpy(s1.name, "张三"); // 字符串需要用 strcpy 赋值
s1.age = 20;
s1.score = 95.5f;
// 4. 访问成员并打印
printf("学号: %d\n", s1.id);
printf("姓名: %s\n", s1.name);
printf("年龄: %d\n", s1.age);
printf("成绩: %.2f\n", s1.score);
return 0;
}
关键特性
- 内存布局:
struct中所有成员在内存中是顺序存储的,整个结构体的大小是其所有成员大小之和(考虑内存对齐)。- 例如上面的
Student结构体,在大多数系统上,int是 4 字节,char[50]是 50 字节,float是 4 字节,总大小至少是 4 + 50 + 4 + 4 = 62 字节。
- 例如上面的
- 成员独立性:所有成员可以同时存在并且可以同时使用,修改一个成员不会影响其他成员的值。
- 访问方式:使用点运算符 来访问成员,如果是指针指向结构体,则使用箭头运算符
->。
typedef 简化
为了避免每次声明变量时都写 struct 关键字,通常使用 typedef 来创建一个类型别名。
typedef struct {
int id;
char name[50];
} Student; // 直接使用 Student 作为类型名
int main() {
Student s2; // 声明变量变得更简洁
s2.id = 1002;
// ...
return 0;
}
union (联合体)
union 也是一种自定义数据类型,但它与 struct 有着根本性的区别。union 的所有成员共享同一段内存空间。

(图片来源网络,侵删)
核心思想
在同一个时间点,你只需要使用一个数据,但这个数据的类型可能不同。union 允许你用多种方式来解释同一块内存,从而节省内存空间。
语法
union 联合体名 {
数据类型 成员1;
数据类型 成员2;
// ...
数据类型 成员n;
};
// 声明联合体变量
union 联合体名 变量名;
示例:处理不同类型的数据
假设一个程序需要处理一个数据,它可能是整数、浮点数或字符串,但任何时候只会处理其中一种,我们可以用 union 来实现。
#include <stdio.h>
#include <string.h>
// 定义一个名为 Data 的联合体
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
// 1. 赋值并使用整数成员
data.i = 10;
printf("data.i: %d\n", data.i);
// 2. 赋值并使用浮点数成员
// 注意:data.str 和 data.i 的值已经被覆盖!
data.f = 220.5;
printf("data.f: %.2f\n", data.f);
// 3. 赋值并使用字符串成员
// data.f 的值已经被覆盖!
strcpy(data.str, "Hello C");
printf("data.str: %s\n", data.str);
// 如果现在尝试打印 data.i 或 data.f,得到的是无意义的值
printf("data.i 的值: %d (无意义)\n", data.i);
printf("data.f 的值: %.2f (无意义)\n", data.f);
return 0;
}
关键特性
- 内存共享:
union的所有成员都从同一个内存地址开始存储,联合体的大小是其最大成员的大小。- 例如上面的
Data联合体,int和float通常是 4 字节,char[20]是 20 字节。union Data的大小是 20 字节。
- 例如上面的
- 互斥性:在任意时刻,只有一个成员的值是有效的,当你给一个成员赋值时,其他成员的值会被覆盖。
- 用途:主要用于以下场景:
- 当你需要处理多种类型的数据,但同一时间只会使用一种时,以节省内存。
- 在底层硬件或网络协议中,同一个内存位置可能需要被解释为不同的数据类型。
struct vs union 核心区别总结
| 特性 | struct (结构体) |
union (联合体) |
|---|---|---|
| 内存分配 | 为每个成员分配独立的内存空间。 | 所有成员共享同一块内存空间。 |
| 总大小 | 所有成员大小的总和(考虑对齐)。 | 其最大成员的大小。 |
| 成员关系 | 所有成员可以同时存在且互不影响。 | 在任意时刻,只有一个成员的值有效,赋值会覆盖其他成员。 |
| 主要用途 | 将相关的不同类型数据组合成一个整体,用于描述一个复杂的对象。 | 在同一块内存上解释不同类型的数据,用于节省内存或处理多种可能的数据格式。 |
| 比喻 | 一个工具箱,里面有锤子、螺丝刀、扳手,你可以同时使用它们。 | 一个插座,你只能插一个电器(台灯或电脑),拔掉一个才能插另一个。 |
深入探讨:内存对齐
为了提高内存访问效率,编译器会对 struct 和 union 的成员进行内存对齐,规则通常是:
- 第一个成员放在偏移量为 0 的地址处。
- 其他成员的偏移量必须是它自身大小的整数倍。
- 整个结构体/联合体的总大小必须是其中最大成员大小的整数倍。
struct 内存对齐示例
struct Example {
char c; // 1 字节
int i; // 4 字节
short s; // 2 字节
};
char c: 放在偏移量 0。int i: 需要从 4 的倍数地址开始,所以偏移量是 4。short s: 需要从 2 的倍数地址开始,当前地址是 4+4=8,是 2 的倍数,所以放在偏移量 8。- 总大小:目前是 1 + 3(填充) + 4 + 2 = 10,但最大成员是
int(4字节),10 不是 4 的倍数,所以需要在末尾填充 2 个字节。 - 最终大小:12 字节。
union 内存对齐示例
union DataExample {
char c;
int i;
float f;
};
union的大小是其最大成员的大小。int和float通常是 4 字节。char c和int i都从偏移量 0 开始。- 最终大小:4 字节。
enum (枚举) - 相关概念
虽然 enum 不是 struct 或 union,但它也是一种创建自定义类型的方式,常与它们一起使用。enum 用于定义一组命名的整型常量。

(图片来源网络,侵删)
// 定义一个颜色枚举
enum Color {
RED, // 默认值为 0
GREEN, // 默认值为 1
BLUE, // 默认值为 2
YELLOW // 默认值为 3
};
// 也可以指定值
enum Status {
OK = 200,
NOT_FOUND = 404,
SERVER_ERROR = 500
};
int main() {
enum Color my_color = GREEN;
printf("my_color 的值: %d\n", my_color); // 输出 1
if (my_color == GREEN) {
printf("颜色是绿色!\n");
}
return 0;
}
enum 通常用于让代码更具可读性,例如用 enum Status 代替魔法数字 200、404。
- 用
struct当你需要一个“容器”来存放一组相关的数据,并且这些数据需要同时存在。Person、Car、Student。 - 用
union当你需要一个“占位符”,这块内存可能被解释成不同类型的数据,但同一时间只使用一种。 处理网络协议包、节省内存空间。 - 用
enum当你需要定义一组有限的、有意义的命名常量时。 颜色、状态、星期等。
理解这三者的区别和适用场景,是编写结构清晰、高效 C 代码的重要基础。
