结构体进阶:结构体嵌套、结构体指针、结构体作为函数参数
说实话,很多初学者学完结构体基础后,就觉得「哦,就是个打包数据的工具嘛」。
但等你真正做项目,你会发现——结构体的玩法远比你想象的深。
我个人习惯把结构体比作「乐高积木」:基础块能拼,但真正厉害的是用大块套小块、用指针指来指去、把块塞进函数里。
今天我们就来聊聊结构体的三个进阶用法:嵌套、指针、作为函数参数。
这三个东西,说白了就是让你能把数据组织得更灵活、更高效。
一、结构体嵌套:大结构套小结构
什么叫嵌套?就是一个结构体里面,又塞了另一个结构体。
举个例子:你要描述一个学生,学生有姓名、学号,还有出生日期。
日期本身又是一个结构体(年、月、日)。这时候嵌套就派上用场了。
struct Date {
int year;
int month;
int day;
};
struct Student {
char name[50];
int id;
struct Date birthday; // 嵌套!
};
我在项目中遇到过类似场景:做员工管理系统,每个员工有入职日期、离职日期、合同到期日期。
如果每个日期都单独写三个 int,代码会变得又臭又长。
用嵌套结构体,清晰多了。
student.birthday.year。别写成
student.year,编译器会骂你的。
二、结构体指针:轻量级操作
结构体本身可能很大。如果你直接传递整个结构体,复制开销不小。
这时候用指针,就相当于只传递一个「门牌号」,而不是把整栋房子搬过去。
struct Student stu = {"张三", 1001, {2000, 5, 20}};
struct Student *p = &stu;
// 两种访问方式:
// 1. (*p).name
// 2. p->name (推荐,更直观)
printf("姓名:%s\n", p->name);
printf("出生年份:%d\n", p->birthday.year);
嗯,这里要注意:p->birthday.year 这种写法,箭头和点号混用,很多人一开始会搞混。
记住一个原则:箭头用于指针,点号用于实体。
结果程序跑着跑着就崩了。后来我养成了习惯:
if (p != NULL) { ... }。你想想看,一个空指针访问成员,那不就是往空气里抓东西吗?
三、结构体作为函数参数
结构体可以传给函数,有两种方式:传值 和 传指针。
传值(复制一份)
void printStudent(struct Student s) {
printf("姓名:%s,学号:%d\n", s.name, s.id);
}
这种方式简单,但效率低。如果结构体很大,每次调用都会复制一整份数据。
传指针(推荐)
void printStudentPtr(const struct Student *p) {
if (p == NULL) return;
printf("姓名:%s,学号:%d\n", p->name, p->id);
}
传指针只复制一个地址(4或8字节),快得多。
加上 const 关键字,表示函数不会修改结构体内容,安全又高效。
- 如果只是读取数据,用
const 指针- 如果需要修改数据,用普通指针
- 如果结构体很小(比如就两个 int),传值也可以
四、综合示例:学生信息管理系统(片段)
#include <stdio.h>
#include <string.h>
struct Date {
int year, month, day;
};
struct Student {
char name[50];
int id;
struct Date birthday;
};
// 初始化学生
void initStudent(struct Student *p, const char *name, int id, int y, int m, int d) {
if (p == NULL) return;
strcpy(p->name, name);
p->id = id;
p->birthday.year = y;
p->birthday.month = m;
p->birthday.day = d;
}
// 打印学生信息
void printStudent(const struct Student *p) {
if (p == NULL) return;
printf("姓名:%s,学号:%d,出生日期:%d-%02d-%02d\n",
p->name, p->id,
p->birthday.year, p->birthday.month, p->birthday.day);
}
int main() {
struct Student stu;
initStudent(&stu, "李四", 1002, 1999, 3, 15);
printStudent(&stu);
return 0;
}
这段代码里,我用了指针传参、嵌套结构体、const 保护。
说白了,这就是实际项目中很常见的写法。
五、知识结构图
下面这张图帮你理清今天讲的核心逻辑:
六、避坑指南
- 嵌套别太深:我见过有人嵌套四五层,代码读起来像迷宫。一般两层就够了,最多三层。
- 指针一定要初始化:要么指向合法地址,要么设为 NULL。野指针是 C 语言的头号杀手。
- 传指针时加 const:如果你不打算修改数据,加上 const 能让代码更安全,也更容易理解。
- 结构体大小要注意:因为内存对齐,结构体实际大小可能比你想象的大。用
sizeof确认一下。
这样能减少内存对齐造成的浪费。虽然不是什么大优化,但积少成多嘛。
好了,今天的内容就到这里。
结构体嵌套、指针、函数参数——这三个东西组合起来,基本能应对大部分数据组织的需求。
你写代码的时候,多想想:数据之间是什么关系?怎么传效率最高?
想清楚了,代码自然就漂亮了。
公众号:蓝海资料掘金营,微信deep3321