iOS内存对齐—结构体

一.数据类型占用内存大小

大家应该都知道在不同CPU架构系统中,各数据类型占用内存空间大小存在一些些差异,不过当今主流系统是64bit,此篇幅就以64bit来讲解,这里附一张各数据类型占内存大小的图,如下:
数据类型占用内存大小.png

二.结构体内存对齐规则

2.1 数据成员对⻬规则

结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组、结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。

2.2 结构体作为成员

如果一个结构中有另外的结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char、int、double等元素,那b应该从8的整数倍开始存储)

2.3 收尾工作

结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬。

三.结构体举例说明

这里罗列了多种类型的结构体,覆盖了数据类型和对齐规则多个组合

3.1 无结构体嵌套

struct WJStructSimple1 {
    BOOL a;                      // 1    [0]
    //总大小:1(为其内部最大成员的整数倍,不足的需补⻬;即 1 * 1 = 1)
};

struct WJStructSimple2 {
    short a;                      // 2    [0 1]
    //总大小:2(为其内部最大成员的整数倍,不足的需补⻬,1非2倍数;即 2 * 1 = 2)
};

struct WJStructSimple3 {
    int a;                      // 4    [0 1 2 3]
    //总大小:4(为其内部最大成员的整数倍,不足的需补⻬,3非4倍数;即 4 * 1 = 4)
};

struct WJStructSimple4 {
    int a;                      // 4    [0 1 2 3]
    short c;                    // 2    [4 5]
    //总大小:8(为其内部最大成员的整数倍,不足的需补⻬,5非4倍数;即 4 * 2 = 8)
};

struct WJStructSimple5 {
    int a;                      // 4    [0 1 2 3]
    short b;                    // 2    [4 5]
    NSString *c;                // 8    (6 7 [8 9 10 11 12 13 14 15]
    //总大小:16(为其内部最大成员的整数倍,不足的需补⻬,15非8倍数;即 8 * 2 = 16)
};

struct WJStructSimple6 {
    int a;                      // 4    [0 1 2 3]
    NSString *b;                // 8    (4 5 6 7 [8 9 10 11 12 13 14 15]
    short c;                    // 2    [16 17]
    //总大小:24(为其内部最大成员的整数倍,不足的需补⻬,17非8倍数;即 8 * 3 = 24)
};

准备打印日志代码

- (void)testStructMemorySize {
    struct WJStructSimple1 simpleStruct1;
    struct WJStructSimple2 simpleStruct2;
    struct WJStructSimple3 simpleStruct3;
    struct WJStructSimple4 simpleStruct4;
    struct WJStructSimple5 simpleStruct5;
    struct WJStructSimple6 simpleStruct6;
    NSLog(@"\n simpleStruct1 size:%lu\n simpleStruct2 size:%lu\n simpleStruct3 size:%lu\n \
simpleStruct4 size:%lu\n simpleStruct5 size:%lu\n simpleStruct6 size:%lu\n",\
          sizeof(simpleStruct1),sizeof(simpleStruct2),sizeof(simpleStruct3),\
          sizeof(simpleStruct4),sizeof(simpleStruct5),sizeof(simpleStruct6));
}

字节占用打印输出如下:

2021-06-14 23:35:38.163203+0800 内存对齐[5288:300199] 
 simpleStruct1 size:1
 simpleStruct2 size:2
 simpleStruct3 size:4
 simpleStruct4 size:8
 simpleStruct5 size:16
 simpleStruct6 size:24

3.2 有结构体嵌套

struct WJStructComplex1 {
    long a;                     // 8    [0 1 2 3 4 5 6 7]
    int b;                      // 4    [8 9 10 11]
    char c;                     // 1    [12]
    short d;                    // 2    (13 [14 15]
    float e;                    // 4    [16 17 18 19]
    struct WJStructSimple4 str; // 8    [20 21 22 23 24 25 26 27]
    //结构体成员str: 需要从其内部最大元素大小的整数倍地址开始存储,最大为4,20是4的倍数可以存,即str开始存储位置为20
    //总大小:32(为其内部最大成员的整数倍,不足的需补⻬,27非8倍数;即 8 * 4 = 32)
};

struct WJStructComplex2 {
    NSString *a;                // 8    [0 1 2 3 4 5 6 7]
    int b;                      // 4    [8 9 10 11]
    char c;                     // 1    [12]
    short d;                    // 2    (13 [14 15]
    float e;                    // 4    [16 17 18 19]
    struct WJStructSimple6 str; // 24   (20 21 22 23 [24 ... 47]
    //结构体成员str: 需要从其内部最大元素大小的整数倍地址开始存储,最大为8,即:8 * 3 = 24, str开始存储位置为24
    //总大小:48(为其内部最大成员的整数倍,不足的需补⻬,47非8倍数;即 8 * 6 = 48)
};

准备打印日志代码

- (void)testStructMemorySize {
    struct WJStructComplex1 complexStruct1;
    struct WJStructComplex2 complexStruct2;
    NSLog(@"\n complexStruct1 size:%lu\n complexStruct2 size:%lu\n",sizeof(complexStruct1),sizeof(complexStruct2));
}

字节占用打印输出如下:

2021-06-14 23:35:38.163329+0800 内存对齐[5288:300199] 
 complexStruct1 size:32
 complexStruct2 size:48

四.结构体代码分析

就以无结构体嵌套和有结构体嵌套中各选一套做分析,其它结构体根据内存对齐规则以此类推。

4.1无结构体嵌套

struct WJStructSimple5 {
    int a;                      // 4    [0 1 2 3]
    short b;                    // 2    [4 5]
    NSString *c;                // 8    (6 7 [8 9 10 11 12 13 14 15]
    //总大小:16(为其内部最大成员的整数倍,不足的需补⻬,15非8倍数;即 8 * 2 = 16)
};

变量a: 占4个字节,offert从0开始, 即[0 1 2 3]存放a;
变量b: 占2个字节,4是2的倍数,所以offert从4开始, 即[4 5]存放b;
变量c: 占8个字节,6不是8的倍数,所以offert需从8开始,(6 7 [8 9 10 11 12 13 14 15]存放c

总大小:16 字节

原因:由于15非最大成员变量8字节倍数,所以需要补齐,8 * 2 = 16

4.2有结构体嵌套

struct WJStructSimple4 {
    int a;                      // 4    [0 1 2 3]
    short c;                    // 2    [4 5]
    //总大小:8(为其内部最大成员的整数倍,不足的需补⻬,5非4倍数;即 4 * 2 = 8)
};
struct WJStructComplex1 {
    long a;                     // 8    [0 1 2 3 4 5 6 7]
    int b;                      // 4    [8 9 10 11]
    char c;                     // 1    [12]
    short d;                    // 2    (13 [14 15]
    float e;                    // 4    [16 17 18 19]
    struct WJStructSimple4 str; // 8    [20 21 22 23 24 25 26 27]
    //结构体成员str: 需要从其内部最大元素大小的整数倍地址开始存储,最大为4,20是4的倍数可以存,即str开始存储位置为20
    //总大小:32(为其内部最大成员的整数倍,不足的需补⻬,27非8倍数;即 8 * 4 = 32)
};

WJStructComplex1分析如下:
变量a: 占8个字节,offert从0开始, 即[0 1 2 3 4 5 6 7]存放a;
变量b: 占4个字节,8是4的倍数,所以offert从8开始,即[8 9 10 11]存放b;
变量c: 占1个字节,offert需从12开始,[12]存放c
变量d: 占2个字节,因13是不是2的倍数,所以offert从14开始, 即(13 [14 15]存放d;
变量e: 占4个字节,16是4的倍数,所以offert从16开始, 即[16 17 18 19]存放e;
变量str: WJStructSimple4结构体占8个字节,需要从其内部最大元素大小的整数倍地址开始存储,最大为4,20是4的倍数,所以offer从可以从20开始,即[20 21 22 23 24 25 26 27]存放str。

总大小:32 字节

原因:由于27非最大成员变量8字节倍数,所以需要补齐,8 * 4 = 32

五.总结

根据结构体内存对齐规则推理出结构体总共占用的内存大小。

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

推荐阅读更多精彩内容