Part 1: struct (结构体)
1 是什么?
结构体是一种“打包”的数据类型,它允许你将多个不同类型的变量组合在一起形成一个单一的、复合的变量,你可以把它想象成一个“记录”或一个“对象”的简单版本,它包含多个字段。

2 为什么用?
当你需要描述一个具有多个属性的事物时,结构体非常适用,一个学生有学号、姓名、年龄、成绩等多个属性,这些属性类型不同,但都属于“学生”这个概念。
3 如何定义和使用?
定义结构体类型:
struct Student {
int id; // 学号
char name[50]; // 姓名
int age; // 年龄
float score; // 成绩
};
这里我们定义了一个名为 struct Student 的数据类型,但还没有创建任何变量。
声明结构体变量:

// 方式一:在定义结构体时直接声明变量
struct Student {
int id;
char name[50];
} student1, student2;
// 方式二:使用已定义的结构体类型声明变量
struct Student student3, student4;
访问结构体成员: 使用成员访问运算符 (点) 来访问和修改结构体内部的变量。
#include <stdio.h>
#include <string.h>
// 定义结构体
struct Student {
int id;
char name[50];
int age;
};
int main() {
// 声明并初始化一个结构体变量
struct Student student1 = {101, "张三", 20};
// 访问并打印成员
printf("学号: %d\n", student1.id);
printf("姓名: %s\n", student1.name);
printf("年龄: %d\n", student1.age);
// 修改成员的值
student1.age = 21;
printf("修改后的年龄: %d\n", student1.age);
return 0;
}
4 内存布局
结构体的内存大小是其所有成员大小的总和,并可能因为内存对齐(Memory Alignment)而存在“空洞”或填充字节,以提升 CPU 访问效率。
struct Example {
char c; // 1 字节
int i; // 4 字节
short s; // 2 字节
};
// 假设对齐方式为 4 字节
// 内存布局: [c][ ][ ][i][i][i][i][s][s][ ]
// 总大小通常是 12 字节,而不是 1+4+2=7 字节。
Part 2: union (联合体)
1 是什么?
联合体是一种“共享”的数据类型,它允许你在同一个内存地址存储不同类型的数据,其核心特点是:所有成员共享同一块内存空间。
2 为什么用?
联合体的主要目的是节省内存,当你知道一个数据块在同一时间只会以一种形式存在时,就可以使用联合体,一个变量可能需要存储一个整数,或者一个字符,或者一个浮点数,但绝不会同时存储它们。

3 如何定义和使用?
定义联合体类型:
union Data {
int i;
float f;
char str[20];
};
声明联合体变量:
// 方式一:在定义时声明
union Data {
int i;
float f;
} data1;
// 方式二:使用已定义的类型声明
union Data data2;
访问联合体成员: 同样使用 运算符。
关键点: 由于所有成员共享内存,你对任何一个成员的写入都会覆盖其他成员的值,读取时,你只能读取最后一次写入的那个类型的数据。
#include <stdio.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);
// 再以浮点数形式写入,这会覆盖 i 的值
data.f = 220.5;
printf("data.f: %f\n", data.f);
// 此时再打印 data.i,其值已经被破坏,是未定义的或毫无意义的
printf("data.i (after f assigned): %d (This is garbage!)\n", data.i);
// 最后以字符串形式写入,这会覆盖 f 的值
strcpy(data.str, "Hello");
printf("data.str: %s\n", data.str);
// 此时再打印 data.f,其值也已经被破坏
printf("data.f (after str assigned): %f (This is also garbage!)\n", data.f);
return 0;
}
4 内存布局
联合体的内存大小是其所有成员中占用空间最大的那个成员的大小,因为所有成员都从同一个内存地址开始存放。
union Example {
char c; // 1 字节
int i; // 4 字节
short s; // 2 字节
};
// 这个联合体的大小是 4 字节,因为 int i 是最大的成员。
// 内存布局: [ ][ ][ ][c/i/s] (所有成员都从第一个字节开始)
Part 3: struct vs union 核心对比
| 特性 | struct (结构体) |
union (联合体) |
|---|---|---|
| 核心思想 | 打包 | 共享 |
| 内存占用 | 所有成员大小的总和 (考虑对齐) | 最大成员的大小 |
| 成员关系 | 每个成员有自己独立的内存空间 | 所有成员共享同一块内存空间 |
| 数据存储 | 可以同时存储所有成员的数据 | 同一时间只能有效存储一个成员的数据 |
| 典型用途 | 描述一个事物的多个属性,如学生、员工信息 | 节省内存,当数据有多种类型但不同时存在时,如数据解析、硬件寄存器访问 |
Part 4: 实际应用场景
结构体 的应用
几乎无处不在,是组织复杂数据的基础。
- 学生信息管理系统
- 图形学中的点坐标
struct Point { int x; int y; }; - 网络编程中的地址
struct sockaddr_in { ... }; - 操作系统中的进程控制块
struct task_struct { ... };
联合体 的应用
场景相对特定,但非常高效。
-
网络数据解析:一个网络数据包的载荷部分可能是整数、浮点数或字符串,你可以用联合体来解析它。
union PacketPayload { int int_value; float float_value; char char_array[128]; }; -
硬件寄存器访问:一个硬件寄存器可能被读写为字节、字或双字,用联合体可以方便地访问。
// 假设 0x1234 是一个硬件寄存器的地址 volatile union { uint8_t byte; uint16_t word; uint32_t dword; } *hw_reg = (union volatile ... *)0x1234; hw_reg->byte = 0xAA; // 以字节方式写入 uint16_t value = hw_reg->word; // 以字方式读取 -
实现“变体”类型:比如一个可以存储多种类型值的变量(类似于 C++ 的
std::variant)。
一个经典的 union + struct 组合:tagged union (带标签的联合体)
这是联合体最强大的用法之一,用于安全地处理多种可能类型的数据,你需要一个额外的 struct 成员(标签)来记录当前联合体中存储的是哪种类型。
#include <stdio.h>
#include <string.h>
// 定义类型标签
typedef enum { TYPE_INT, TYPE_FLOAT, TYPE_STRING } DataType;
// 定义带标签的联合体
typedef struct {
DataType type; // 标签,指示当前有效的是哪个成员
union {
int i_val;
float f_val;
char* s_val;
} data;
} Variant;
void printVariant(Variant v) {
switch (v.type) {
case TYPE_INT:
printf("Integer: %d\n", v.data.i_val);
break;
case TYPE_FLOAT:
printf("Float: %f\n", v.data.f_val);
break;
case TYPE_STRING:
printf("String: %s\n", v.data.s_val);
break;
default:
printf("Unknown type\n");
}
}
int main() {
Variant v1, v2, v3;
v1.type = TYPE_INT;
v1.data.i_val = 123;
v2.type = TYPE_FLOAT;
v2.data.f_val = 456.789f;
v3.type = TYPE_STRING;
v3.data.s_val = "Hello, Union!";
printVariant(v1);
printVariant(v2);
printVariant(v3);
return 0;
}
这个模式非常强大,是许多动态类型语言和高级数据结构实现的基础。
- 用
struct当你想把几个东西“放在一起”,形成一个整体,它们会各自占据自己的空间。 - 用
union当你想让几个东西“共用一个地方”,节省空间,但要知道同一时间只能用其中一个。
理解它们的区别是掌握 C 语言内存管理和数据组织的关键一步。
