iOS底层探索--内存对齐

1,480 阅读5分钟

对于大多数研究底层的程序员,对内存对其应该都不陌生,今天小生来献丑一波。

内存对齐的定义

内存对齐 应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上。
对于大部分研究过底层的程序员来说,“内存对齐”对他们来说都应该是“透明的”。

为什么要内存对齐

由于本人文采不好,借用下百度百科的解释。

  • 1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  • 2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

内存对齐的规则

(很长,说实话,我也懒得看下去。。没关系,后面我会举例解释)

  • 1:数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第 一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要 从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组, 结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存 储。 min(当前开始的位置mn) m=9 n=4 那么 9 10 11 12 、 就要从12开始存储
  • 2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从 其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b 里有char,int ,double等元素,那b应该从8的整数倍开始存储.)
  • 3:收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大 成员的整数倍.不足的要补⻬

各类型所占字节

我也知道大家这个表都知道,但是不把图啪上感觉没有仪式感

举例说明内存对齐

上面罗里吧嗦的终于结束了。只看理论啥收获都没有感觉,还是不让代码验证解释比较痛快。

我会通过结构体来说明

  • 例1:
注: {}代表解释说明 。
struct XGStruct1 {
    double a;   // 8{所占字节数} (0{开始位置}-7{结束位置}){所占字节位置}
    char b;     // 1{所占字节数} [8{开始位置} 1{所占字节数}] (8){存储字节位置}
    int c;      // 4{所占字节数} [9{开始位置} 4{所占字节数}] 9 10 11 (12 13 14 15){存储字节位置}
    short d;    // 2{所占字节数} [16{开始位置} 2{所占字节数}] (16 17){存储字节位置}
}struct1;

解释说明:
内存对齐规则1:开始位置必须是所占字节数的整数倍,才能开始存。
(举例:char b,由于开始位置是所占字节数的整数倍可以直接存,int c ,开始位置是从9,并不是4的整数倍,所以从12开始存)

存储大小是:18{0-17}

由于结构体中存在double为最大字节数:8
(内存对齐规则3:所占内存必须是最大字节数的整数倍。所以必须是8的整数倍。18-->24)

所以所占内存大小是24.
输出log确认下:
NSLog(@"%lu",sizeof(struct1));

log输出:24
  • 例2:

这个应该就比较形象了。

struct XGStruct2 {
    int b;      //4{所占字节数} [0{开始位置} 4{所占字节数}],(0 1 2 3){存储字节位置}
    char c;     //1{所占字节数} [4{开始位置} 1{所占字节数}], (4){存储字节位置}
    short d;    //2{所占字节数} [5{开始位置} 2{所占字节数}], 5 (6 7){存储字节位置}
    short e;    //2{所占字节数} [8{开始位置} 2{所占字节数}], (8 9){存储字节位置}
}struct2;

解释说明:
1、内存所占大小是 100-92、内存中最大的字节是int4),所以是4的整数倍。
理论结果:12

验证:
NSLog(@"%lu",sizeof(struct2));
输出:
12

内存对齐规则的扩展

以上2个例子应该可以说明内存对其规则了。我相信大家也都懂了。那么,咱们来一波进阶:结构体嵌套结构体。

继续用上面两个例子,然后加以改正:

struct XGStruct1 {
    double a; 
    char b;  
    int c;     
    short d;    
}struct1;
//已证

struct XGStruct2 {
    int b;      
    char c;     
    short d;    
    short e;    
}struct2;
//已证

我们把XGStruct2 改变下,加入XGStruct1,变成XGStruct3:
struct XGStruct3 {
    int b;      
    char c;     
    short d;    
    short e;
    struct XGStruct1 xgs;
}struct3;

大家可以先想想下,所占内存大小?

解释了啊:

首先。我们先通过规则,大概猜一下,会是什么样子的。。

然后我要开始验证了啊。。

先换一种好说明的写法
struct XGStruct3 {
    int b;      //4{所占字节数} [0{开始位置} 4{所占字节数}],(0 1 2 3){存储字节位置}
    char c;     //1{所占字节数} [4{开始位置} 1{所占字节数}], (4){存储字节位置}
    short d;    //2{所占字节数} [5{开始位置} 2{所占字节数}], 5 (6 7){存储字节位置}
    short e;    //2{所占字节数} [8{开始位置} 2{所占字节数}], (8 9){存储字节位置}
    struct XGStruct4 {
        double a;// [10 8] 10 11 12 13 14 15 (16 17 18 19 20 21 22 23)
        int b;   // [24 4] (24 25 26 27)
        char c;  // [28 1] (28)
        short d; // [29 2] 29 (30 31)
    }xgs;
}struct3;

所占大小是320-31)
里面最大类型是double8),又是8的倍数,那岂不是结果是32,

验证一波:
NSLog(@"%lu",sizeof(struct3));
输出:
32

还真是!!!!!

大家可以通过多写点例子来验证。。看来内存对齐的规则还是挺好用的。。