












在计算机科学中,内存对齐是一种数据存储方式,它要求某些类型的数据必须存储在特定地址上。这种要求是由硬件架构决定的,不同的处理器可能有不同的对齐要求
内存对齐的目的和作用: 性能提升和平台兼容性
性能提升 -- 减少CPU的访问次数,优化内存带宽利用
平台兼容性 -- 硬件限制和移植性
C++中的内存对齐原则
C++标准规定了每个数据类型的最小对齐要求,通常为该类型大小的倍数. 比如,int类型(假设为4字节)应该至少4字节对齐,double类型(假设为8字节)应该至少8字节对齐
基本数据类型:对于内置类型如 char, short, int, long, float, double等,它们的对齐方式通常是其自身大小
结构体和类:对于复合类型,像结构体和类,它的整体对齐要求是其所有成员中最大的那个,按照它的对齐大小进行对齐.
数组:数组的对齐方式和其元素类型相同
**特别注意**: 对齐数 = 编译器默认的一个对齐数与该成员大小的较小值
比如 Visual Studio的默认对齐数是8
我们现在来看一个例子
#include <iostream> #include <type_traits> struct MyStruct { char a; // 1 byte int b; // 4 byte double c; // 8 byte short d; // 2 byte }; int main() { // struct结构体中每个成员的偏移量 std::cout << "offset of a is " << offsetof(MyStruct, a) << std::endl; std::cout << "offset of b is " << offsetof(MyStruct, b) << std::endl; std::cout << "offset of c is " << offsetof(MyStruct, c) << std::endl; std::cout << "offset of d is " << offsetof(MyStruct, d) << std::endl; //struct结构体中每个成员的内存对齐要求 std::cout <<"Alignment of char " << alignof(a) << std::endl; std::cout <<"Alignment of char " << alignof(b) << std::endl; std::cout <<"Alignment of char " << alignof(c) << std::endl; std::cout <<"Alignment of char " << alignof(d) << std::endl; //struct结构体的大小和对齐要求 std::cout << "size of MyStruct " << sizeof(MyStruct) << std::endl; std::cout << "Alignment of MyStruct " << alignof(MyStruct) << std::endl; return 0; }
我们来看结构体的内存对齐原则
1. 每个成员必须在自身对齐大小的整数倍偏移地址
2. 结构体的整体对齐大小 = 结构体内部最大成员的对齐大小 (这里是 double = 8)
3. 结构体的总大小必须是整体对齐大小的整数倍,也就是结构体内部最大成员内存对齐大小的整数倍. => 这里也就是8的整数倍
MyStruct的char型成员变量a
大小: 1
对齐: 1
偏移: 0
占用: 0 (占用偏移为0的位置)
MyStruct的int型成员变量b
对齐: 4 -必须放置在偏移为4的位置. a只占0~1,所以要填充3字节(1,2,3)
偏移: 4
占用: 4-7
MyStruct的double型成员变量c
对齐: 8 -必须放置在偏移为8的位置
偏移: 8
占用: 8-15
MyStruct的short型成员变量d
对齐: 2
偏移:16 (也就是d的起始位置)
占用: 16-17
结构体MyStruct的整体大小
最终 sizeof (MyStruct) = 24
我们再来看几个例子,以便加深理解
以下几个例子,默认64位系统,自然对齐
关于C++中的虚函数,回顾的话可以看看我写的文章 https://www.cnblogs.com/wphl-27/p/18113373
class Test { char a; int b; virtual void virFunc(); }; 求 sizeof(Test);
分析:
Test类中存在一个虚函数,它就会有一个8字节的指向虚函数表的指针vptr(注意,类中无论有多少个虚函数,都只有一个8字节的指针vptr) -> 具体可以参考我写的博客 https://www.cnblogs.com/wphl-27/p/18113373
vptr -> 0-7
char-> 8-9
int-> 占4位,所以必须是4的倍数开头,所以内存对齐填充 10,11
int 是 12-15
所以总的内存大小是 0-15, 共16, 它是类的自身对齐值8 (类中内部最大成员vptr的占位大小)的倍数, 所以就算16
例子2 - 单继承 + 虚函数
class A { virtual void f1(); char a; }; class B : public A { int b; double c; }; // 求 sizeof(A)、sizeof(B)
sizeof(A)
一个虚函数有指针vptr -> 0-7
char a -> 7-8
所以总的内存大小是 0-8, 共9, 它不是类的自身对齐值8 (类中内部最大成员vptr的占位大小)的倍数, 所以要补齐,成为8的倍数
补 9-15, 共16
所以 sizeof(A) = 16
sizeof(B)
继承A的成员
vptr -> 0-7
char a -> 7-8
int b -> 占4位,所以必须是4的倍数开头,所以内存对齐填充 9,10,11
int 是 12-15
double c -> 16-23
所以总的内存大小是 0-23, 共24, 它是类的自身对齐值8 (类中内部最大成员vptr的占位大小)的倍数, 所以就算24
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。