struct (结构体)
核心思想:打包
结构体就像一个打包盒,你可以把不同类型、不同大小的变量(int, char, float)打包在一起,形成一个整体,这些变量在内存中是独立存放的,互不干扰。

(图片来源网络,侵删)
内存布局
结构体的总大小是其所有成员大小之和(可能会有内存对齐填充)。
语法
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
// ...
};
// 定义结构体变量
struct 结构体名 变量名;
// 或者
struct {
数据类型 成员1;
数据类型 成员2;
} 变量名; // 匿名结构体
代码示例
#include <stdio.h>
// 定义一个学生结构体
struct Student {
int id; // 4字节
char name[50]; // 50字节
float score; // 4字节
};
int main() {
// 声明一个结构体变量 s1
struct Student s1;
// 访问成员使用点运算符 .
s1.id = 101;
sprintf(s1.name, "Zhang San"); // 安全的字符串赋值方式
s1.score = 95.5f;
printf("Student ID: %d\n", s1.id);
printf("Student Name: %s\n", s1.name);
printf("Student Score: %.2f\n", s1.score);
// 计算结构体大小
printf("Size of struct Student: %zu bytes\n", sizeof(struct Student)); // 输出通常是 58 (4+50+4 + 对齐填充)
return 0;
}
主要用途
- 组织相关数据:将逻辑上相关的数据项组合成一个单元,一个学生的信息、一个汽车的配置等。
- 函数参数:将多个相关参数作为一个结构体传递,比传递多个独立参数更清晰。
- 返回值:函数可以返回一个包含多个值的结构体。
union (联合体)
核心思想:共享
联合体就像一个共享的储物格,但这个储物格的“形状”可以变化,它允许你在同一个内存地址上存储不同类型的数据,这些成员是共享同一块内存空间的。
内存布局
联合体的总大小是其最大成员的大小(同样可能有内存对齐填充),任何时候,只有一个成员的数据是有效的。
语法
union 联合体名 {
数据类型 成员1;
数据类型 成员2;
// ...
};
// 定义联合体变量
union 联合体名 变量名;
// 或者
union {
数据类型 成员1;
数据类型 成员2;
} 变量名; // 匿名联合体
代码示例
#include <stdio.h>
// 定义一个数据联合体
union Data {
int i;
float f;
char str[20];
};
int main() {
// 声明一个联合体变量 data
union Data data;
// 访问成员同样使用点运算符 .
// 注意:每次只能使用一个成员,赋值会覆盖其他成员的值
data.i = 10;
printf("data.i: %d\n", data.i);
data.f = 220.5;
printf("data.f: %f\n", data.f); // data.i 的值已经被破坏了
printf("data.i (after f assignment): %d (garbage value)\n", data.i);
// 你不能同时安全地使用 str 和 i 或 f
// strcpy(data.str, "Hello C");
// printf("data.str: %s\n", data.str);
// printf("data.i (after str assignment): %d (garbage value)\n", data.i);
// 计算联合体大小
printf("Size of union Data: %zu bytes\n", sizeof(union Data)); // 输出通常是 20 (str[20]是最大的)
return 0;
}
主要用途
- 节省内存:当你有一组数据,但在任何时刻只会使用其中一个时,使用联合体可以大大节省内存,一个变量可能是整数也可能是浮点数,但你不会同时需要它的两种表示。
- 类型双关:允许以不同的数据类型解释同一块内存,这在底层编程、硬件交互或网络协议解析中非常常见。
- 构建“变体”类型:可以和
struct结合,创建一个可以存储不同类型数据的结构,一个struct有一个type字段和一个union字段,union中根据type的值来决定使用哪个成员。
struct vs union 核心区别总结
| 特性 | struct (结构体) |
union (联合体) |
|---|---|---|
| 核心思想 | 打包 | 共享 |
| 内存布局 | 所有成员在内存中连续、独立存放,所有成员同时存在且有效。 | 所有成员共享同一块内存空间,任何时刻只有一个成员的值是有效的。 |
| 总大小 | 所有成员大小之和(考虑内存对齐)。 | 最大成员的大小(考虑内存对齐)。 |
| 成员访问 | 可以同时访问所有成员,互不影响。 | 一次只能访问一个成员,对某个成员赋值会覆盖其他成员的值。 |
| 主要用途 | 组织和管理相关的、需要共存的数据。 | 节省内存;实现类型双关;处理多种可能的数据格式。 |
经典应用场景:struct + union
这是联合体最强大的用法之一,常用于实现“变体对象”(Variant Object)或标记联合(Tagged Union)。

(图片来源网络,侵删)
假设我们要设计一个可以表示不同类型几何图形的数据结构,一个图形可以是圆形、矩形或三角形,但它们的数据不同。
#include <stdio.h>
#include <string.h>
// 定义联合体,用于存放不同图形的数据
union ShapeData {
// 圆形: 只需要半径
struct {
float radius;
} circle;
// 矩形: 需要长和宽
struct {
float length;
float width;
} rectangle;
// 三角形: 需要底和高
struct {
float base;
float height;
} triangle;
};
// 定义枚举,标记当前图形的类型
enum ShapeType {
SHAPE_CIRCLE,
SHAPE_RECTANGLE,
SHAPE_TRIANGLE
};
// 定义主结构体,包含类型标记和共享数据
struct Shape {
enum ShapeType type; // 标记位
union ShapeData data; // 共享的数据
};
// 计算面积的函数
float calculateArea(struct Shape* s) {
switch (s->type) {
case SHAPE_CIRCLE:
return 3.14159f * s->data.circle.radius * s->data.circle.radius;
case SHAPE_RECTANGLE:
return s->data.rectangle.length * s->data.rectangle.width;
case SHAPE_TRIANGLE:
return 0.5f * s->data.triangle.base * s->data.triangle.height;
default:
return 0.0f;
}
}
int main() {
// 创建一个圆形
struct Shape myCircle;
myCircle.type = SHAPE_CIRCLE;
myCircle.data.circle.radius = 10.0f;
printf("Circle Area: %.2f\n", calculateArea(&myCircle));
// 创建一个矩形
struct Shape myRectangle;
myRectangle.type = SHAPE_RECTANGLE;
myRectangle.data.rectangle.length = 5.0f;
myRectangle.data.rectangle.width = 8.0f;
printf("Rectangle Area: %.2f\n", calculateArea(&myRectangle));
// 错误示范:如果误用类型,后果严重
// myCircle.type = SHAPE_CIRCLE;
// printf("Rectangle's radius: %.2f\n", myCircle.data.circle.radius); // 这是无意义的,因为myCircle.data里存的是rectangle的数据
return 0;
}
在这个例子中:
struct Shape是外层容器,它知道当前存储的是哪种图形(type)。union ShapeData是真正的“变体”部分,它根据type的值来决定如何解读其内部的数据。- 这种模式非常安全且高效,避免了使用
void*指针带来的风险。
- 用
struct当你想把几个东西放在一起,并且同时需要使用它们。 - 用
union当你有几个东西,但同一时间只用其中一个,想节省空间或者想用不同方式看待同一块内存。
理解这两者的根本区别——内存是独立打包还是共享——是掌握 C 语言数据类型的关键一步。
