结构体与共用体:把数据组织起来
说实话,刚学C++那会儿,我总觉得结构体这玩意儿有点多余。不就是把几个变量捆在一起吗?后来做项目才明白——没有结构体,你写代码会累死。想想看,你要描述一个学生,有姓名、年龄、学号、成绩……难道每次都定义四个独立变量?那要是一百个学生呢?
结构体就是用来解决这个问题的。它让你能把不同类型的数据打包成一个整体。嗯,咱们今天就把这块彻底讲透。
结构体的定义与使用
结构体的定义很简单,用 struct 关键字就行。我个人习惯把结构体定义放在头文件里,这样多个源文件都能用。
struct Student {
char name[32]; // 姓名
int age; // 年龄
char id[20]; // 学号
float score; // 成绩
}; // 别忘了分号!
定义好之后怎么用?直接声明变量,然后用点号 . 访问成员:
Student stu1; // 声明一个学生
strcpy(stu1.name, "张三");
stu1.age = 20;
strcpy(stu1.id, "2024001");
stu1.score = 88.5f;
// 也可以声明时直接初始化
Student stu2 = {"李四", 21, "2024002", 92.0f};
小技巧:我建议结构体成员命名时加个前缀,比如 m_name 或 name_,这样在代码里一眼就能看出是成员变量。这个习惯帮我省了不少调试时间。
结构体数组:批量管理数据
一个学生好办,一百个学生呢?用结构体数组啊。说白了,就是每个数组元素都是一个结构体。
Student class1[100]; // 一个班最多100人
// 给第一个学生赋值
strcpy(class1[0].name, "王五");
class1[0].age = 19;
class1[0].score = 78.0f;
// 遍历所有学生
for (int i = 0; i < 100; i++) {
if (class1[i].score >= 60.0f) {
printf("%s 及格了\n", class1[i].name);
}
}
我在项目中遇到过一个问题:结构体数组初始化时忘了清零,结果有些成员是随机值,排查了好久才发现。所以建议你声明数组后,用 memset 或循环初始化一下。
结构体指针:灵活又高效
结构体指针这东西,刚开始容易搞混。其实记住一点就行:指针用 -> 访问成员,普通变量用 .。
Student stu = {"赵六", 22, "2024003", 85.0f};
Student* pStu = &stu;
// 两种写法等价
printf("姓名:%s\n", (*pStu).name); // 先解引用,再访问
printf("姓名:%s\n", pStu->name); // 直接用箭头,更简洁
为什么要用指针?两个场景:
- 函数传参:传指针比传整个结构体快得多,尤其是结构体很大时
- 动态内存:用
new在堆上创建结构体,必须用指针操作
// 函数传参用指针
void printStudent(const Student* p) {
printf("%s, %d岁, 成绩%.1f\n", p->name, p->age, p->score);
}
// 动态创建
Student* p = new Student;
strcpy(p->name, "孙七");
p->age = 23;
printStudent(p);
delete p; // 别忘了释放
注意:用 new 创建的结构体,一定要用 delete 释放。我曾经在项目里漏了一次,结果内存泄漏,程序跑了两天就崩了。嗯,从那以后我每次 new 完立刻写 delete,形成肌肉记忆。
共用体:省空间的妙招
共用体 union 和结构体长得很像,但有个关键区别:所有成员共享同一块内存。也就是说,你改了其中一个,其他的也跟着变。
union Data {
int i;
float f;
char str[20];
};
Data d;
d.i = 10;
printf("d.i = %d\n", d.i); // 10
d.f = 3.14f;
printf("d.f = %f\n", d.f); // 3.14
printf("d.i = %d\n", d.i); // 乱码!因为内存被f覆盖了
共用体的大小等于最大成员的大小。上面这个 Data 占 20 字节(因为 str 最大)。
什么时候用共用体?我一般在两种场景用:
- 协议解析:同一个数据块,有时当整数读,有时当字节数组读
- 节省内存:多个成员不会同时使用,比如一个设备要么是温度传感器,要么是湿度传感器
// 实际项目中的例子:解析网络数据包
union PacketHeader {
uint32_t raw; // 当作32位整数
uint8_t bytes[4]; // 当作4个字节
};
PacketHeader h;
h.raw = 0x12345678;
// 可以通过h.bytes[0]拿到最低字节,方便做字节序转换
核心区别总结:
| 特性 | 结构体 struct | 共用体 union |
|---|---|---|
| 内存分配 | 各成员独立,总大小 = 各成员之和(考虑对齐) | 所有成员共享,总大小 = 最大成员 |
| 同时使用 | 所有成员可同时有效 | 同一时刻只有一个成员有效 |
| 典型用途 | 组织复杂数据 | 节省内存、类型转换 |
知识体系总览
下面这张图把结构体和共用体的核心知识点串起来了。你看一眼就能明白它们之间的关系。
我的建议:刚开始学的时候,先把结构体用熟。共用体在实际项目中用得相对少一些,但一旦遇到协议解析、数据转换的场景,它比任何方法都优雅。我做过一个嵌入式项目,用共用体解析串口协议,代码量直接减半。
好了,结构体和共用体就讲到这里。记住一句话:结构体是把数据组织起来,共用体是把内存省下来。多写几个例子,自然就熟练了。
公众号:蓝海资料掘金营,微信deep3321