内存对齐规则

1、内存对齐规则

  • 数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第 一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置
    该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,
    结构体等)的整数倍开始存储(比如int为4字节,则要从4的整数倍地址开始存储)。
  • 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小整数倍地址开始存储。(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储)。
  • 结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍。不足的要补⻬。
  • 关键点:
    • 第一个数据成员放在offset为0的地方
    • 以后起始位置为该成员大小的整数倍
    • 结构体成员:起始位置为子结构体最大成员的整数倍
    • 结构体总大小为最大成员整数倍
各类型字节长度

接下来我们先从一个简单结构体进行分析理解这个规则

数据成员对⻬规则

struct KKStruct1 {
    double a;
    char b;
    int c;
    short d;
}struct1;
  • 第一步 接下来我们按照上述规则进行填充。
    • 第一个成员double 占8字节放在offset为0的地方。所占区域[0 - 7]
    • 第二个成员char占1字节填充的起始位置char 1字节的整数倍。所占区域[8]
    • 第三个成员int占4字节填充的起始位置int 4字节的整数倍。所以空出接下来的非4的整数倍的区域[9,10,11],从12开始填充所占区域[12,13,14,15]
    • 第四个成员short占2字节填充的起始位置short 2字节的整数倍。所占区域[16,17];

根据规则填充得到结构体struct1所需要内存18。但是分配内存需要按照最大成员8的整数倍应分配得24
运行结果图1显示与我们按照规则计算的相同。

图1

  • 第二步为结构体struct1赋值,查看值存储情况进行进一步的验证
    由于double类型在二进制中不方便观察。这里我们将double类型改为NSInteger类型。注意NSInteger 在64位中占8字节,32位中占4字节。使用时确定使用的为64位设备
//结构体指针的内存大小 8
struct KKStruct1 {
//    double a;//8 [0 - 7]
    NSInteger a;
    char b;//1 [8]
    int c;//4 (9,10,11) [12,13,14,15]
    short d;//2 [16 17]
}struct1;//8的倍数 24
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        struct1.a = 13;
        struct1.b = 'c';
        struct1.c = 11;
        struct1.d = 9;
        NSLog(@"%p",&struct1);
        NSLog(@"%zd",sizeof(struct1));
        NSLog(@"Hello, World!");
    }
    return 0;
}

按照上面规则,我们输出的内存中存储数据应如结果图2

图2

接下来我们读区内存中的数据检查是否是我们存储的数据结果图3

A4CB2945-8555-4FE3-8CD7-C4CB4C9A50F7.png

从结果中可以看到第一个和第四个确实是存储的成员数据a = 13,d = 9。但是第二个变成了一非常大的数据。
这是因为在读取时按照一个固定的部长8字节进行读取的。所以读取的地址其实是存储着b和c两个成员的内存地址图4。取出地址在分开读区即可。这里输出的b = 99这里99是字符c对应的ASCII码
图4

结构体作为成员

这里我们用沿用上面的已知结构体struct1作为研究对象。另定义一个结构体struct2作为struct1的成员验证上述的规则。

  • 第一步 定义简单的结构体struct2,一个成员char
    struct2只有一个成员char。占1字节
    将其作为struct1的成员。按照规则结构体成员要从其内部最大元素大小的整数倍地址开始存储进行填充。struct2中只有char 1字节。所以在struct1的最后一个成员后面进行存储填充即可。
    这里为了方便观察,为struct1的成员dstruct2的成员b进行赋值。
//结构体指针的内存大小 8
struct KKStruct2 {
    char b;//1 [0]
}struct2;//1

struct KKStruct1 {
//    double a;//8 [0 - 7]
    NSInteger a;
    char b;//1 [8]
    int c;//4 (9,10,11) [12,13,14,15]
    short d;//2 [16 17]
    struct KKStruct2 s2;//内部成员最大1,[18]
}struct1;//8的倍数 24

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"%p",&struct1);
        NSLog(@"%zd",sizeof(struct1));
    }
    return 0;
}

按照规则struct2内部成员最大1字节的整数倍。填充存储。struct1的所需内存不需要发生变化的。
结果图2.1

图2.1

从结果图来开struct2的成员是在struct1的成员d后面开始填充存储的。

  • 第二步 为struct2添加成员int类型4字节来进行观察
    此时struct2中最大成员为int类型4字节。所以struct2中的第一个成员char存储的起始位置为int类型4字节的整数倍
    struct1的d成员结尾为17,将空余18,19struct2char20开始存储,空余21,22,23,int24开始存储24,25,26,27
    此时struct1的内存应为32
struct KKStruct2 {
    char b;//1 [0]
    int e;//4 空(123)[4 - 7]
}struct2;//8

struct KKStruct1 {
//    double a;//8 [0 - 7]
    NSInteger a;
    char b;//1 [8]
    int c;//4 (9,10,11) [12,13,14,15]
    short d;//2 [16 17]
    struct KKStruct2 s2;//最大为4 空(18,19)char从20开始,空(21,22,23),int [24,25,26,27]
}struct1;//8的倍数 32

结果图2.2


图2.2

struct1的所需内存变成了28。最大8字节的整数倍应为32

  • 注意:成员结构体struct2也需要满足结构体内存对齐原则
struct KKStruct2 {
    char b;//1 [0]
    int e;//4 空(123)[4 - 7]
    short c;//2 [8,9]
}struct2;//12

struct KKStruct1 {
    int c;//4 [0-3]
    double a;//8 (5,6,7)[8 - 15]
    char b;//1 [16]
    struct KKStruct2 s2;//最大为4 空(17,18,19),char[20],空(21,22,23),int [24,25,26,27] short[28,29],(内存对齐扩容(30,31))
    short d;//2 [32 33]
}struct1;//8的倍数 40


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        struct1.d = 2;
        NSLog(@"struct1 :%zd  struct2:%zd",sizeof(struct1),sizeof(struct2));
        NSLog(@"%p",&struct1);
    }
    return 0;
}
结果图
内存信息图

结构体struct2填充到29就结束了,但是结构体需满足内存对齐原则。所以为其扩容(30,31)总共占12字节,其最大成员的整数倍数,所以short32开始填充。内存为40字节

  • struct2其内成员的最大字节是影响struct1大小的关键。

  • 由于第一个成员之后的成员开始存储的位置为该成员大小的整数倍,所以后面难免会出现不是该成员大小(1,2,4,8)的整数倍的情况。造成空余的位置。因此结构体成员的顺序会因空余位置造成所需内存大小的不同。

    结果图

可以发现调整结构体成员的顺序,从而将中间空余的部分给利用了起来。从而节省了结构体所需的内存。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。