美文网首页
内存对齐

内存对齐

作者: milawoai | 来源:发表于2020-09-08 17:36 被阅读0次

    内存对齐指的是结构体中对结构成员内存的一系列调整。通过调整offset位置,减少读取结构成员数据需要的CPU-> 内存读取次数。任何对象数据在底层实际上是结构体。

    对齐规则

    1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行

    2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行

    3、结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

    是不是有一点眼晕?我自己总结的计算方法如下:

    内存对齐原则 (简化版):

    1. 找到最大的基础类型成员 , 将其内存长度作为基准
    2. 每个成员的offset,需要是成员自己类型的整数倍
    3. 放完之后,struct自身的数据需要为基准的整数倍

    让我们看一个简单的数据:

    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
    
    

    如果我们稍微调整下数据顺序:

    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. 找到最大的基础类型成员 , 将其内存长度作为基准 (如果成员为数组,基准不按数组总长,而是按照数组元素类型来计算)
    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 基准的整数倍而不是总长的整数倍)
    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

    2.内存分布查看方法:

    po &struct4 -> 0x00000001014cc600
    
    x/8gx 0x00000001014cc600
    

    相关文章

      网友评论

          本文标题:内存对齐

          本文链接:https://www.haomeiwen.com/subject/mmfjektx.html