结构体嵌套:把结构体装进结构体
结构体嵌套,说白了就是一个结构体里面再塞一个结构体。我刚开始学的时候觉得这玩意儿有点绕,后来在项目中处理学生信息时才发现——真香。
举个例子,你要描述一个学生。学生有姓名、年龄,还有家庭住址。地址本身又包含省、市、区、街道。你总不能把地址拆成四个独立字段吧?那代码写出来自己都嫌丑。
struct Address {
char province[32];
char city[32];
char district[32];
char street[64];
};
struct Student {
char name[32];
int age;
struct Address addr; // 嵌套结构体
};
这样一写,逻辑就清晰了。访问的时候用 student.addr.city,一层一层点进去,跟剥洋葱似的。
结构体自引用:结构体里藏着自己
自引用,就是结构体里有一个指针,指向跟自己同类型的结构体。你想想看,这玩意儿最经典的应用场景是什么?链表嘛!
struct Node {
int data;
struct Node *next; // 指向下一个节点
};
这里有个坑,我当年踩过。你不能写成 struct Node next; 而不是指针。为什么?因为编译器在解析 struct Node 的时候,它的大小还没确定。你放一个完整的结构体进去,那就成了无限递归——结构体里套结构体,永远算不出大小。
自引用在树、图这些数据结构里也随处可见。比如二叉树节点:
struct TreeNode {
int value;
struct TreeNode *left;
struct TreeNode *right;
};
每个节点知道自己左边是谁、右边是谁,整个树就串起来了。
typedef 类型重定义:给结构体起个外号
每次写 struct Student stu; 你不觉得烦吗?反正我烦。尤其是结构体名字一长,敲起来手指头都累。这时候 typedef 就派上用场了。
typedef struct Student {
char name[32];
int age;
} Student; // 以后直接用 Student 就行
更简洁的写法是这样:
typedef struct {
char name[32];
int age;
} Student;
匿名结构体 + typedef,一步到位。以后声明变量直接 Student stu;,干净利落。
我在项目中经常用 typedef 给复杂类型起别名。比如函数指针:
typedef int (*CompareFunc)(const void *, const void *);
这样写回调函数的时候,代码可读性直接拉满。
三者结合:一个实际例子
咱们把嵌套、自引用、typedef 揉在一起,写一个简单的链表节点:
typedef struct Node {
int data;
struct Node *next; // 自引用
} Node;
typedef struct {
Node *head; // 嵌套指针
int size;
} LinkedList;
你看,Node 自引用指向下一个节点,LinkedList 嵌套了 Node * 作为头指针。整个链表的结构就出来了。
嗯,这里要注意:自引用里必须用 struct Node *,不能用 Node *。因为 typedef 还没生效呢,编译器还不认识 Node 这个别名。
知识结构图
避坑指南
- 嵌套别太深:超过三层嵌套,代码就难读了。我一般控制在两层以内,再多就拆成指针或者单独的结构体。
- 自引用必须是指针:这个前面说过了,再强调一遍——不是指针就等着编译报错吧。
- typedef 和自引用的顺序:自引用里不能用 typedef 之后的别名,因为别名还没定义。老老实实写
struct XXX *。 - 匿名结构体慎用:如果你只需要一个结构体变量,匿名没问题。但要是想定义多个,或者用在函数参数里,还是起个名字吧。
好了,结构体的嵌套、自引用、typedef 就聊到这儿。这三个东西单独看都不难,但组合起来威力很大。你在项目里遇到复杂数据关系时,想想今天讲的这些,应该能帮你理清思路。
公众号:蓝海资料掘金营,微信deep3321