在 C++ 中,类(class
)的内存对齐会在以下情况下触发:
- 类的成员变量类型具有对齐要求。
- 类中包含继承关系时,基类与派生类的对齐可能受影响。
- 内存对齐受架构(如 32 位、64 位)和编译器的实现策略影响。
下面详细解析 什么时候会触发内存对齐 以及 内存对齐的规则。
内存对齐触发的条件
1. 类的成员变量类型的对齐要求
每种数据类型都有其对齐要求(alignment requirements),编译器会将类的成员变量按照其对齐要求进行排列,必要时填充字节(padding)以满足对齐规则。
对齐的关键点:
- 数据类型的对齐要求是由其大小决定的,例如:
char
通常为 1 字节对齐。int
通常为 4 字节对齐。double
通常为 8 字节对齐(在 64 位系统上)。
- 成员变量的起始地址必须是其对齐要求的倍数。
例子:
分析:
char a
占 1 字节。- 接下来的
int b
需要对齐到 4 字节,因此在char a
后插入 3 字节的填充。 char c
占 1 字节,但类的大小最终需要满足最严格的成员对齐(int
的对齐是 4 字节),因此添加填充到总大小为 12 字节。
输出结果:
2. 基类和派生类的对齐
当类有继承关系时,基类的对齐要求会影响派生类的内存布局,通常派生类的起始地址需要满足基类的对齐要求。
例子:
分析:
Base
只有一个double
,占 8 字节。Derived
需要满足Base
的 8 字节对齐,因此char c
后会填充 7 字节,保证整个Derived
的大小是Base
的倍数。
输出结果:
3. 虚函数表指针(vptr)的对齐
如果类中包含虚函数,编译器会为每个对象增加一个虚函数表指针(vptr
),这个指针通常遵循指针类型的对齐要求(通常是 4 字节或 8 字节)。
例子:
分析:
Base
中有虚函数,因此包含一个vptr
指针(64 位系统上占 8 字节)。Derived
继承了虚函数表指针,同时增加了char c
,因此最终大小需要对齐到 8 字节。
输出结果:
4. 对齐受类对齐属性(alignment specifier)影响
C++11 提供了 alignas
关键字,可以显式设置类或变量的对齐要求。
例子:
输出结果:
5. 编译器和目标平台对齐策略
- 自然对齐(natural alignment):
大多数编译器会默认对齐到数据类型的大小倍数(如int
为 4 字节对齐)。 - 强制对齐(forced alignment):
某些平台可能强制对齐到更大的倍数(如 64 位系统可能对齐到 8 字节)。 - 数据结构的整体对齐:
类的大小会向最严格对齐要求对齐。
如何查看类的内存布局
- 使用
sizeof
查看类的大小: - 使用
offsetof
查看成员偏移: - 使用调试工具(如 GDB)查看布局。
总结
- 类的内存对齐会在以下场景触发:
- 成员变量的类型有对齐要求。
- 类中包含继承关系。
- 类有虚函数表指针。
- 显式使用
alignas
设置对齐。
- 影响内存对齐的因素:
- 成员变量的类型。
- 编译器实现和目标平台。
- 类的继承关系和虚函数。
- 优化建议:
- 将对齐要求较高的成员变量放在类的开头,减少填充字节。
发表回复