一、结构体基础:把零散数据装进一个“盒子”
大家好,我是老李。今天咱们聊聊结构体。
说实话,我刚入行那会儿,对结构体也没啥感觉。觉得不就是把几个变量捆在一起嘛,有啥了不起的?直到有一次做嵌入式项目,要管理几十个传感器的数据——温度、湿度、气压、时间戳……每个传感器都有一堆属性。用单独的数组?那代码写得我想砸键盘。后来用了结构体,整个世界清静了。
结构体,说白了就是自定义的数据类型。C语言给了你int、char、float这些基本积木,但现实世界的数据往往是复合的。一个学生有学号、姓名、成绩;一个传感器有ID、数值、状态。结构体就是让你把这些“一家人”整整齐齐放在一起。
核心思想:结构体 = 把不同类型的数据打包成一个新的数据类型。
1.1 结构体的定义
定义结构体,就像画一张“蓝图”。告诉编译器:我要组合哪些成员。
// 定义一个学生类型
struct Student {
int id; // 学号
char name[20]; // 姓名
float score; // 成绩
}; // 别忘了这个分号!
嗯,这里要注意:struct是关键字,Student是结构体标签(tag),花括号里是成员列表。分号不能丢,我见过太多新手在这里栽跟头。
你也可以用typedef给它起个“别名”,这样后面用起来更顺手:
typedef struct {
int id;
char name[20];
float score;
} Student; // 现在 Student 就是一个类型名了
我个人习惯用typedef版本,少敲几个struct关键字,代码也更清爽。
1.2 结构体变量的初始化与访问
定义好了类型,接下来就是创建变量。初始化方式有好几种,我挑最常用的说。
方式一:定义时直接初始化
Student stu1 = {1001, "张三", 89.5};
顺序必须和成员声明一致。你想想看,如果写成{89.5, "张三", 1001},编译器会傻眼的。
方式二:指定成员初始化(C99起支持)
Student stu2 = {
.id = 1002,
.name = "李四",
.score = 92.0
};
这种方式更安全,顺序无所谓,可读性也强。我在项目中经常用,尤其是结构体成员很多的时候。
访问成员:用点号(.)
printf("学号:%d\n", stu1.id);
printf("姓名:%s\n", stu1.name);
printf("成绩:%.1f\n", stu1.score);
// 也可以修改
stu1.score = 95.0;
点号操作符,简单直接。就像打开一个盒子,拿出里面的东西。
小技巧:如果结构体里有数组成员,不能直接赋值。比如 stu1.name = "王五"; 是错的。要用 strcpy(stu1.name, "王五");
1.3 结构体数组
一个学生不够?那就来一打。结构体数组,就是结构体类型的数组。
// 定义一个班级,有3个学生
Student class[3] = {
{1001, "张三", 89.5},
{1002, "李四", 92.0},
{1003, "王五", 78.5}
};
// 遍历访问
for (int i = 0; i < 3; i++) {
printf("第%d个学生:%s,成绩:%.1f\n",
i+1, class[i].name, class[i].score);
}
结构体数组在嵌入式开发中太常见了。比如管理多个按键的状态、多个传感器的配置参数。我曾经做一个智能家居项目,用结构体数组管理了32个传感器的配置信息,代码量直接砍半。
1.4 结构体指针
指针来了。结构体指针,就是指向结构体的指针。
Student stu = {1001, "张三", 89.5};
Student *p = &stu; // p 指向 stu
// 通过指针访问成员:两种方式
printf("学号:%d\n", (*p).id); // 方式一:先解引用,再点号
printf("学号:%d\n", p->id); // 方式二:箭头操作符(推荐)
箭头操作符->,是专门为结构体指针设计的。它等价于(*p).成员,但写起来更直观。我个人几乎不用(*p).这种写法,太啰嗦了。
避坑指南:我曾经在项目里犯过一个低级错误——用结构体指针时忘了初始化。声明了 Student *p; 就直接 p->id = 1001;,结果程序跑飞了。记住:指针必须指向有效的内存地址才能使用!
结构体指针在函数传参时特别有用。你想想看,如果直接把结构体传给函数,会发生什么?
// 传值:整个结构体被拷贝,开销大
void printStudent(Student s) {
printf("%s\n", s.name);
}
// 传指针:只传地址,效率高
void printStudentPtr(const Student *s) {
printf("%s\n", s->name);
}
尤其是结构体很大(比如几十个成员),传值会导致栈空间暴涨。传指针,加上const修饰防止意外修改,是嵌入式开发的标配写法。
知识体系总览
下面这张图,帮你把结构体的核心知识点串起来:
小结
结构体不是什么高深的东西。它就是C语言给你的一个“打包工具”。把相关的数据装在一起,代码更清晰,维护更方便。
记住几个要点:
- 定义时别忘了分号
- 访问成员用点号,指针访问用箭头
- 函数传参尽量用指针,省内存、效率高
- 结构体数组适合管理同类型的一批对象
好了,这一章就到这里。结构体是C语言里非常实用的工具,后面讲链表、队列、树这些数据结构,都离不开它。先把基础打牢,后面才能飞起来。