结构体与联合体:C++中的复合数据类型
大家好,我是你们的老朋友。今天我们来聊聊C++里两个非常基础但又容易踩坑的概念——结构体(struct)和联合体(union)。嗯,别小看它们,我见过不少项目因为结构体对齐问题导致内存泄漏,也见过有人把union用错导致数据覆盖。咱们今天就把这些坑一个个填平。
一、结构体:struct的定义与使用
结构体说白了就是把多个不同类型的数据打包成一个整体。比如你要描述一个学生,有姓名、年龄、分数,用结构体就很方便。
struct Student {
char name[32];
int age;
float score;
}; // 别忘了分号!
我个人习惯在定义结构体时,把成员变量按从大到小排列,这样能减少内存对齐带来的浪费。你想想看,如果先放char再放int,中间会多出几个字节的填充,白白浪费空间。
使用结构体也很简单:
Student stu1; // C++中可以直接用Student,不用加struct
stu1.age = 20;
stu1.score = 95.5f;
strcpy(stu1.name, "张三");
Student stu2 = {"李四", 22, 88.0f};
二、内存对齐:看不见的填充字节
这里有个问题:结构体占用的内存大小,真的等于所有成员大小之和吗?
答案是否定的。因为CPU读取内存时,是按“字”来读的,不是按字节。为了让CPU读得快,编译器会在成员之间插入一些填充字节,这就是内存对齐。
举个例子:
struct Example1 {
char a; // 1字节
int b; // 4字节
char c; // 1字节
};
你猜这个结构体占多少字节?不是1+4+1=6,而是12!为什么会这样?因为int b需要4字节对齐,所以a后面会填充3个字节;c后面又会填充3个字节,让整个结构体大小是4的倍数。
| 成员 | 偏移量 | 实际占用 |
|---|---|---|
| char a | 0 | 1字节 |
| 填充 | 1-3 | 3字节 |
| int b | 4 | 4字节 |
| char c | 8 | 1字节 |
| 填充 | 9-11 | 3字节 |
#pragma pack(1)强制1字节对齐,或者手动调整成员顺序。
如果你想让结构体更紧凑,可以把相同类型的成员放在一起:
struct Example2 {
char a; // 1字节
char c; // 1字节
int b; // 4字节
}; // 现在只占8字节,省了4字节!
三、联合体:union的概念与用途
联合体和结构体长得很像,但有一个本质区别:所有成员共享同一块内存。也就是说,union的大小等于它最大成员的大小。
union Data {
int i;
float f;
char str[20];
}; // 大小是20字节(str最大)
你想想看,如果你同时给i和f赋值,后赋值的会覆盖前面的。所以union通常用于“多选一”的场景,比如一个变量在不同情况下表示不同类型的数据。
我在项目中遇到过用union解析网络协议的情况。比如一个数据包的前4个字节,可能是整数,也可能是IP地址的四个部分。用union就能轻松转换:
union IPAddress {
uint32_t addr;
uint8_t octets[4];
};
IPAddress ip;
ip.addr = 0xC0A80101; // 192.168.1.1
// 现在ip.octets[0]是192,octets[1]是168,octets[2]是1,octets[3]是1
四、枚举类型:enum与enum class
枚举类型用来定义一组命名的整型常量。比如一周的七天、颜色、状态码等。
enum Color {
RED,
GREEN,
BLUE
}; // RED=0, GREEN=1, BLUE=2
但传统的enum有个问题:它的作用域是全局的。如果你在另一个地方也定义了RED,就会冲突。而且它隐式转换为整数,容易引发bug。
C++11引入了enum class,解决了这些问题:
enum class Color {
RED,
GREEN,
BLUE
};
enum class TrafficLight {
RED, // 不会和Color::RED冲突
GREEN,
YELLOW
};
// 使用时要加作用域
Color c = Color::RED;
// int x = c; // 错误!不能隐式转换
int x = static_cast<int>(c); // 必须显式转换
我个人强烈建议:新代码一律使用enum class。传统enum的隐式转换太容易出问题了。我记得有一次,一个同事把枚举值直接当数组下标用,结果数组越界,查了半天才发现。
enum class Color : uint8_t { ... },这样能节省内存,特别适合嵌入式开发。
五、知识体系总览
下面这张图帮你理清今天讲的内容:
好了,今天的内容就到这里。结构体和联合体虽然基础,但用好它们能让你的代码更高效、更安全。记住:结构体用对齐,union用覆盖,枚举用class。下次写代码时,多想想这些细节,能省不少调试时间。
公众号:蓝海资料掘金营,微信deep3321