1. 结构体基础:为什么需要结构体、结构体的定义与声明、结构体变量的初始化
为什么我们需要结构体?
先问大家一个问题:如果你要描述一个学生,需要姓名、学号、年龄、成绩。你会怎么做?
用几个独立的变量?像这样:
char name[20];
int id;
int age;
float score;
嗯,一个学生还行。那如果有100个学生呢?你得写100组这样的变量。更麻烦的是,你要把某个学生的信息传给函数,得传4个参数。想想就头疼。
我早年做嵌入式开发时,遇到过这样一个场景:要管理一个传感器节点的所有状态——温度、湿度、电压、信号强度、时间戳。一开始我用了一堆全局变量,结果代码越写越乱。后来改用结构体,整个代码瞬间清爽了。
结构体的本质,就是把一组相关的数据打包成一个整体。 说白了,就是C语言提供的一种「自定义数据类型」的能力。你可以把不同类型的数据组合在一起,形成一个新的类型。
核心价值: 结构体让数据有了「归属感」。每个数据都知道自己属于哪个对象,代码的可读性和可维护性大幅提升。
结构体的定义与声明
结构体的定义,语法其实很简单:
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
// ...
};
注意最后的分号,很多人会漏掉。我刚开始学的时候也犯过这个错,编译器报错找了半天。
举个例子,定义一个学生结构体:
struct Student {
char name[20];
int id;
int age;
float score;
};
这里 struct Student 就是一个新的数据类型。你可以用它来声明变量:
struct Student stu1; // 声明一个学生变量
struct Student stu2, stu3; // 声明多个
我个人习惯把结构体定义放在头文件里,这样多个源文件都能用。但要注意,结构体定义只是告诉编译器这个类型长什么样,并不分配内存。只有当你用这个类型声明变量时,才会真正分配内存空间。
结构体变量的初始化
初始化结构体变量,有几种常见方式。我按使用频率给大家排个序:
方式一:按成员顺序初始化
struct Student stu1 = {"张三", 1001, 20, 88.5};
这是最直接的方式。花括号里的值按成员定义的顺序依次赋值。但有个坑——如果结构体成员顺序变了,所有初始化代码都得改。我在项目中吃过这个亏,后来就很少用了。
方式二:指定成员初始化(C99起支持)
struct Student stu2 = {
.name = "李四",
.id = 1002,
.age = 21,
.score = 92.0
};
这种方式更安全,也更清晰。你不需要记住成员的顺序,想初始化哪个就写哪个。没指定的成员会被自动初始化为0(对于指针则是NULL)。
我的建议: 在团队项目中,尽量使用指定成员初始化。代码自文档化,别人一看就知道每个字段的含义。
方式三:先声明,再逐个赋值
struct Student stu3;
strcpy(stu3.name, "王五");
stu3.id = 1003;
stu3.age = 19;
stu3.score = 76.5;
这种方式适合在运行时动态设置值。注意,结构体变量之间可以直接赋值:
struct Student stu4 = stu1; // 拷贝整个结构体
这一点很方便,但要注意:如果结构体里有指针成员,这种浅拷贝会导致多个变量指向同一块内存。我曾经在项目中因为这个问题,排查了一个下午的bug。
结构体的内存布局
这里有个重要的概念——内存对齐。先看个例子:
struct Example {
char c; // 1字节
int i; // 4字节
short s; // 2字节
};
你觉得这个结构体占多少字节?1+4+2=7?不对。实际上,在我的机器上它占12字节。
为什么会这样?因为编译器会在成员之间插入填充字节,让每个成员的地址对齐到它的自然边界。int类型需要4字节对齐,所以char后面会填充3个字节。
| 成员 | 偏移量 | 占用字节 |
|---|---|---|
| char c | 0 | 1 |
| 填充 | 1-3 | 3 |
| int i | 4 | 4 |
| short s | 8 | 2 |
| 填充 | 10-11 | 2 |
避坑指南: 在嵌入式系统中,如果你需要把结构体直接写入EEPROM或通过通信协议发送,内存对齐会导致数据错位。我曾经在调试一个串口通信协议时,发现接收到的数据总是乱码,最后发现是结构体对齐搞的鬼。解决方案是用 #pragma pack(1) 强制1字节对齐。
知识体系总览
下面这张图,把本章的核心知识点串起来了:
小结
结构体是C语言中组织数据的核心工具。它把零散的变量打包成一个整体,让代码更清晰、更易维护。定义时注意分号,初始化时推荐指定成员方式,使用时要留意内存对齐的影响。
嗯,这些基础掌握了,后面讲结构体指针、位域、以及结构体与联合体的配合使用时,你就会觉得顺理成章了。