结构体进阶:结构体嵌套、结构体指针、结构体作为函数参数

说实话,很多初学者学完结构体基础后,就觉得「哦,就是个打包数据的工具嘛」。
但等你真正做项目,你会发现——结构体的玩法远比你想象的深。
我个人习惯把结构体比作「乐高积木」:基础块能拼,但真正厉害的是用大块套小块、用指针指来指去、把块塞进函数里。

今天我们就来聊聊结构体的三个进阶用法:嵌套、指针、作为函数参数
这三个东西,说白了就是让你能把数据组织得更灵活、更高效。

一、结构体嵌套:大结构套小结构

什么叫嵌套?就是一个结构体里面,又塞了另一个结构体。
举个例子:你要描述一个学生,学生有姓名、学号,还有出生日期
日期本身又是一个结构体(年、月、日)。这时候嵌套就派上用场了。

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 这种写法,箭头和点号混用,很多人一开始会搞混。
记住一个原则:箭头用于指针,点号用于实体

我曾经踩过的坑:用指针访问结构体成员时,忘了检查指针是否为 NULL。
结果程序跑着跑着就崩了。后来我养成了习惯: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 保护。
说白了,这就是实际项目中很常见的写法。

五、知识结构图

下面这张图帮你理清今天讲的核心逻辑:

结构体进阶 结构体嵌套 大结构套小结构 如:Student 包含 Date 访问:s.birthday.year 结构体指针 轻量级操作 用箭头 -> 访问成员 注意判空! 作为函数参数 传值 vs 传指针 推荐 const 指针 效率高、安全 核心思想:灵活组织数据 + 高效传递 嵌套让数据有层次,指针让操作更轻量,函数让逻辑可复用

六、避坑指南

  • 嵌套别太深:我见过有人嵌套四五层,代码读起来像迷宫。一般两层就够了,最多三层。
  • 指针一定要初始化:要么指向合法地址,要么设为 NULL。野指针是 C 语言的头号杀手。
  • 传指针时加 const:如果你不打算修改数据,加上 const 能让代码更安全,也更容易理解。
  • 结构体大小要注意:因为内存对齐,结构体实际大小可能比你想象的大。用 sizeof 确认一下。
我的一个小习惯:写结构体时,我会把大的成员放在前面,小的放后面。
这样能减少内存对齐造成的浪费。虽然不是什么大优化,但积少成多嘛。

好了,今天的内容就到这里。
结构体嵌套、指针、函数参数——这三个东西组合起来,基本能应对大部分数据组织的需求。
你写代码的时候,多想想:数据之间是什么关系?怎么传效率最高?
想清楚了,代码自然就漂亮了。


公众号:蓝海资料掘金营,微信deep3321