内存对齐

内存对齐指的是结构体中对结构成员内存的一系列调整。通过调整offset位置,减少读取结构成员数据需要的CPU-> 内存读取次数。任何对象数据在底层实际上是结构体。 ### 对齐规则 > 1、[数据成员](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E6%88%90%E5%91%98)对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐**按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行**。 > 2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将**按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行**。 > 3、结合1、2可推断:当#pragma pack的n值等于或超过所有[数据成员](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E6%88%90%E5%91%98)长度的时候,这个n值的大小将不产生任何效果。 是不是有一点眼晕?我自己总结的计算方法如下: **内存对齐原则 (简化版):** > 1. 找到最大的基础类型成员 , 将其内存长度作为基准 > 2. 每个成员的offset,需要是成员自己类型的整数倍 > 3. 放完之后,struct自身的数据需要为基准的整数倍 让我们看一个简单的数据: ```C++ struct Struct1 { double a; // 8 char b; // 1 int c; // 4 short d; // 2 }struct1; 1. 最长的元素是double,基准为8 2. struct Struct1 { double a; // 长度为8 offset:0,0是8的整数倍,所以 a存放位置为 (0 - 7) char b; // 长度为1 offset: 8 8是 1的整数倍,所以 b存放位置为 (8) int c; // 长度为4 offset: 9 9不是4的整数倍,offset后移到 12 ,所以 c存放位置为 (12 - 15) short d; // 长度为2 offset: 16 16是2的整数倍,所以 d存放位置为 (16 - 17) }struct1; 3. 最终数据成员的整体内存占用位置为 (0 - 17),18个字节。18不是基准 8 的整数倍,因此填充到24. 最终 sizeof(struct1) 的大小为 24 ``` 如果我们稍微调整下数据顺序: ```C++ struct Struct2 { double a; // 8 // 这里调整了 b,c的顺序 int b; // 4 char c; // 1 short d; // 2 }struct2; 1. 最长的元素是double,基准为8 2. struct Struct2 { double a; // 长度为8 offset:0,0是8的整数倍,所以 a存放位置为 (0 - 7) int b; // 长度为4 offset: 8 8是 4的整数倍,所以 b存放位置为 (8 - 11) char c; // 长度为1 offset:12 12是1的整数倍,所以 c存放位置为 (12) short d; // 长度为2 offset: 13 13不是2的整数倍,offset后移到 14 所以 d存放位置为 (14 - 15) }struct2; 3. 最终数据成员的整体内存占用位置为 (0 - 15),16个字节。16是基准 8 的整数倍,因此不需要填充。 最终 sizeof(struct2) 的大小为 16 ``` ### 成员为数组的情况: **内存对齐原则 ( ex1):** > 1. 找到最大的基础类型成员 , 将其内存长度作为基准 (如果成员为数组,基准不按数组总长,而是按照数组元素类型来计算) ```C++ struct Struct3 { char a; // 1 (0) int b[2]; // b0 b1 double c; //8 }struct3; 1. 最长的元素是double,基准为8 2. struct Struct3 { char a; // 长度为1 offset:0 0是1的整数倍,所以 a存放位置为 (0) int b[2]; // b0 长度为4 0ffset:1 1不是4的整数倍,b0的offest后移到4,所以 b0存放位置为 (4 - 7)b1存放位置为 (8 - 11) double c; //长度为8 offset: 12 12不是8的整数倍,offset后移到 16 所以 d存放位置为 (16 - 23) }struct3; 3. 最终数据成员的整体内存占用位置为 (0 - 23),24个字节。24是基准 8 的整数倍,因此不需要填充。 最终 sizeof(struct3) 的大小为 24 其实数组写法很类似下面的写法: struct Struct3 { char a; int b0; int b1; double c; }struct3; 只要调整对了数组第一个元素的offset,数组后续元素自然可以对齐。 ``` ### 成员为struct的情况: > 1. 找到最大的基础类型成员 , 将其内存长度作为基准 (如果成员为struct,基准不按子struct总长,而是max(子struct基准,剩余成员内存长度)) > 2. 每个成员的offset,需要是成员自己类型的整数倍 (如果成员为struct, 只需是 成员struct 基准的整数倍而不是总长的整数倍) ```c++ struct Struct1 { double a; // 8 char b; // 1 int c; // 4 short d; // 2 }struct1; struct Struct4 { int b; // 4 // (0-3) char c; // 1 // (4) struct Struct1 s1; // a:8 (8 - 15) b:1 (16) c:4 (20 - 23) d:2 (24-25) (8 - 25) 18字节 -> short d; // 2 // 38 (36 - 37) }struct4; 1. Struct1的基准为8,除Struct1 s1外,Struct4最长的元素是int 长为4,基准为max(8,4) = 8 2. struct Struct4 { int b; // 4 // offset:0 ,存放位置: (0-3) char c; // 1 // offset:4,存放位置: (4) struct Struct1 s1; /** a: 长度: 8 offset:5 -> 8 存放位置: (8 - 15) b:长度: 1 offset:16 存放位置:(16) c: 长度: 4 offset:17 -> 20 存放位置: (20 - 23) d: 长度: 2 offset:24 存放位置: (24-25) s1 总体位置:(8 - 25) 18字节 -> 填充 (8 - 31)24字节 **/ short d; // 2 // offset:32,存放位置: (32 - 33) }struct4; 3. 总体位置 (0 - 33) 长度为 34,不是8的倍数,填充到 40 最终 sizeof(struct4) 的大小为 40 ``` **附表:** 1. 基础数据表 ![image.png](https://upload-images.jianshu.io/upload_images/2413806-fd6fb982fdf62dc3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 2.内存分布查看方法: ```shell po &struct4 -> 0x00000001014cc600 x/8gx 0x00000001014cc600 ```

你可能感兴趣的:(内存对齐)