内存对齐原则
-
数据成员对⻬规则
:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)。 -
结构体作为成员
:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a里存有struct b,b里有char、int 、double等元素,那b应该从8的整数倍开始存储)。 -
结构体的总大小
,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬。
验证对齐规则
结构体嵌套成员变量
我们通过定义两个结构体,分别计算他们的内存大小
//1、定义两个结构体
struct Mystruct1{
char a; //1字节
double b; //8字节
int c; //4字节
short d; //2字节
}Mystruct1;
struct Mystruct2{
double b; //8字节
int c; //4字节
short d; //2字节
char a; //1字节
}Mystruct2;
//计算 结构体占用的内存大小
NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));
输出结果如下所示:
我们可以通过下图图来说明下为什么两个结构体MyStruct1 & MyStruct2的内存大小打印不一致的情况,如图所示
计算结构体MyStruct1的大小
-
变量a
:占1个字节,为第一个成员变量,因此从0开始,此时min(0,1),即0位置存储a
。 -
变量b
:占8个字节,从1开始计算,此时min(1,8),因为1/8=0不是整数倍,所以要先偏移到(8,8),即8-15位置存储b
。 -
变量c
:占4个字节,从16开始计算,此时min(16,4),因为16/4=4是整数倍,所以无需偏移,即16-19位置存储c
。 -
变量d
:占2个字节,从20开始计算,此时min(20,2),因为20/2=10是整数倍,所以无需偏移,即20-21位置存储d
。
此时MyStruct1的实际所需内存大小为22个字节
,但因为结构体的总大小必须是其内部最大成员的整数倍
,且MyStruct1中最大成员变量为b占8字节
,所以MyStruct1的内存大小只能向上取整为8*3=24
字节。
计算结构体MyStruct2的大小
-
变量b
:占8个字节,为第一个成员变量,因此从0开始,此时min(0,8),即0-7位置存储b
。 -
变量c
:占4个字节,从8开始计算,此时min(8,4),因为8/8=2是整数倍,所以无需偏移,即8-11位置存储c
。 -
变量d
:占2个字节,从12开始计算,此时min(12,2),因为12/2=6是整数倍,所以无需偏移,即12-13位置存储d
。 -
变量a
:占1个字节,从14开始计算,此时min(14,1),因为14/1=14是整数倍,所以无需偏移,即14位置存储a
。
此时MyStruct2的实际所需内存大小为15个字节
,但因为结构体的总大小必须是其内部最大成员的整数倍
,且MyStruct2中最大成员变量为b占8字节
,所以MyStruct2的内存大小只能向上取整为8*2=16
字节。
结构体嵌套结构体
首先定义一个结构体MyStruct3,在MyStruct3中嵌套MyStruct2,如下所示
//1、结构体嵌套结构体
struct Mystruct3{
double b; //8字节
int c; //4字节
short d; //2字节
char a; //1字节
struct Mystruct2 str;
}Mystruct3;
//2、打印 Mystruct3 的内存大小
NSLog(@"Mystruct3内存大小:%lu", sizeof(Mystruct3));
NSLog(@"Mystruct3中结构体成员内存大小:%lu", sizeof(Mystruct3.str));
输出结果如下所示:
计算结构体MyStruct3的大小
-
变量b
:占8个字节,为第一个成员变量,因此从0开始,此时min(0,8),即0-7位置存储b
。 -
变量c
:占4个字节,从8开始计算,此时min(8,4),因为8/8=2是整数倍,所以无需偏移,即8-11位置存储c
。 -
变量d
:占2个字节,从12开始计算,此时min(12,2),因为12/2=6是整数倍,所以无需偏移,即12-13位置存储d
。 -
变量a
:占1个字节,从14开始计算,此时min(14,1),因为14/1=14是整数倍,所以无需偏移,即14位置存储a
。 -
结构体成员str
:str是一个结构体,根据内存对齐原则二,结构体成员要从其内部最大成员大小的整数倍开始存储
,而MyStruct2中最大的成员大小为8
,所以str要从8的整数倍开始,当前是从15开始,但不符合要求,需要往后移动到16,16是8的整数倍,符合内存对齐原则,即 16-31位置存储 str
。
此时MyStruct3的实际所需内存大小为 32字节,而MyStruct3中最大变量为str,其最大成员内存字节数为8
,所以 MyStruc3 实际的内存大小必须是 8 的整数倍
,32正好是8的整数倍,所以sizeof(MyStruct3) 的结果是 32
。
其内存存储情况如下所示: