一、什么是内存对齐?
内存对齐是一种在计算机内存中排列数据
(表现为变量的地址)、访问数据
(表现为CPU读取数据)的一种方式。
它包含了两种相互独立又相互关联的部分:基本数据对齐
和结构体数据对齐
。
二、为什么要进行内存对齐?
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
说的通俗点就是方便读取
,速度快
。
三、内存对齐原则:
1、数据成员对⻬规则:结构(struct)
(或联合(union)
)的数据成员,第一个数据成员放在offset为0
的地方,以后每个数据成员存储的起始位置
要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍
开始(比如int为4字节,则要从4的整数倍地址开始存储)。下面咱们用(m, n)
来理解一下,其中m
为内存的起始位置
,n
为当前成员的内存大小
,m
,n
一定要满足m%n==0
。
2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员
要从其内部最大元素大小
的整数倍地址
开始存储(struct a里存有struct b,b里有char、int 、double等元素,那b应该从8的整数倍开始存储。)
3、收尾工作:结构体的总大小
,也就是sizeof
的结果,必须是其内部最大成员
的整数倍
,不足
的要补⻬
。
各位同学,能理解上面这些原则吗,反正光看文字我是理解不了,没关系,咱们是程序员
,咱们可以用代码来解释。
四、数据成员结构体内存分析
struct Struct1 {
double a; // 8
char b; // 1
int c; // 4
short d; // 2
}struct1;
struct Struct2 {
double a; //8
int b; //4
char c; //1
short d; //2
}struct2;
NSLog(@"结果为:%lu-%lu",sizeof(struct1),sizeof(struct2));
咱们来看下打印结果
结果为:24-16
为什么会出现不同的结果呢,咱们来分析一下,不过在分析代码之前呢咱们先看下各个类型的内存大小
下面咱们来分析一下
Struct1
的结果:
- 首先
Struct1
中以double
开始,在内存中的地址是0~7
。 - 接下来是
char
,char
占用内存大小为1
字节,此时在内存中的起始地址
为8
,此时8%1==0
表示可以从此位置开始存放char
,在内存中的地址是8
。 - 然后是
int
,int
占用内存大小为4
字节,此时在内存中的起始位置
为9
,但是现在9%4 != 0
,所以此时只能向后移,找到一个能被4
整除的数,下一个能被4
整除的数是12
,所以此时int
的起始位置为12
,长度为4
字节,在内存中的地址是12~15
。 - 最后是
short
,short
占用内存2
字节,此时内存中的起始位置
为16
,16%2==0
成立,所以short
在内存中的起始位置是16
,长度为2
,在内存中的地址是16~17
。
由上述结果可得Struct1
在内存中占用18
个字节,根据内存对齐第三条原则,Struct1
内部最大成员为double
为8
字节,所以Struct1
最终占用的内存大小为24
字节。由此可以得出下图:
同理,我们再来分析一下Struct2
的结果:
- 首先
Struct2
中以double
开始,在内存中的地址是0~7
。 - 然后是
int
,int
占用内存大小为4
字节,此时在内存中的起始位置
为8
,8%4 == 0
等式成立,所以此时int
的起始位置为8
,长度为4
字节,在内存中的地址是8~11
。 - 接下来是
char
,char
占用内存大小为1
字节,此时在内存中的起始地址
为12
,此时12%1==0
等式成立表示可以从此位置开始存放char
,在内存中的地址是12
。 - 最后是
short
,short
占用内存2
字节,此时内存中的起始位置
为13
,13%2==0
等式不成立,需要向后移,找到一个能被2
整除的数,所以short
在内存中的起始位置是14
,长度为2
,在内存中的地址是14~15
。
有上述结果可得Struct2
在内存中占用16
个字节,根据内存对齐原则
第三条,Struct2
内部最大成员为double
为8
字节,所以Struct2
最终占用的内存大小为16
字节。由此可得出下图:
这就是Struct1
和Struct2
结果不同的原因。
由此我们可以得出结论:结构体所占内存大小与结构体内部的成员变量的顺序有关
。
五、嵌套结构体内存分析
struct Struct3 {
double a; //8
char b; //4
struct Struct1 c; //24
short d; //2
}struct3;
struct Struct4 {
double a; //8
char b; //1
short c; //2
struct Struct2 d; //16
}struct4;
NSLog(@"结果为:%lu-%lu",sizeof(struct3),sizeof(struct4));
咱们来看下打印结果
结果为:48-32
我们来分析下Struct3
的结果:
- 首先
Struct3
中以double
开始,在内存中的地址是0~7
。 - 接下来是
char
,char
占用内存大小为1
字节,此时在内存中的起始地址
为8
,此时8%1==0
等式成立表示可以从此位置开始存放char
,在内存中的地址是8
,在内存中的地址是8
。 - 然后是结构体
Struct1
,由上面的结论得知Struct1
的大小为24
字节,此时在内存中的起始地址
为9
,根据内存对齐原则
第二条得知需从其内部最大元素大小
的整数倍地址
开始存储,Struct1
中最大的为double
占8字节
,9%8 == 0
不成立,所以取最近的一个能被8
整除的数为16
,所以Struct1
在内存中的起始位置为16
,长度为24
,在内存中的地址是16~39
。 - 最后是
short
,short
占用内存2
字节,此时内存中的起始位置
为40
,40%2==0
等式成立,起始位置是40
,长度为2
,在内存中的地址是40~41
。
有上述结果可得Struct3
在内存中占用42
个字节,根据内存对齐原则
,Struct3
内部包括子成员Struct1
内部最大成员为double
为8
字节,所以Struct3
最终占用的内存大小为48
字节。由此可得出下图:
我们来分析下Struct4
的结果:
- 首先
Struct4
中以double
开始,在内存中的地址是0~7
。 - 接下来是
char
,char
占用内存大小为1
字节,此时在内存中的起始地址
为8
,此时8%1==0
等式成立表示可以从此位置开始存放char
,在内存中的地址是8
,在内存中的地址是8
。 - 然后是
short
,short
占用内存2
字节,此时内存中的起始位置
为9
,9%2==0
等式不成立,其后能被2
整除的数为10
,那么起始位置是10
,长度为2
,在内存中的地址是10~11
。 - 最后是结构体
Struct2
,由上面的结论得知Struct2
的大小为16
字节,此时在内存中的起始地址
为12
,根据内存对齐原则
第二条得知需从其内部最大元素大小
的整数倍地址
开始存储,Struct2
中最大的为double
占8字节
,12%8 == 0
不成立,所以取最近的一个能被8
整除的数为16
,所以Struct2
在内存中的起始位置为16
,长度为16
,在内存中的地址是16~31
。
有上述结果可得Struct4
在内存中占用32
个字节,根据内存对齐原则
,Struct4
内部包括子成员Struct1
内部最大成员为double
为8
字节,所以Struct4
最终占用的内存大小为32
字节。由此可得出下图:
以上就是我对内存对齐原则
的理解,如果有不同意见的同学欢迎留言给我。