内存对齐的原则:
- 数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第
一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要
从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,
结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存
储。 - 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从
其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b
里有char,int ,double等元素,那b应该从8的整数倍开始存储.) - 收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬。
举个例子:
struct Struct1 {
char a; //1字节
double b; //8字节
short c; //2字节
int d; //4字节
} Struct1;
struct Struct2 {
char a; //1字节
short b; //2字节
int c; //4字节
double d; //8字节
} Struct2;
NSLog(@"Struct1占用内存大小:%lu",sizeof(Struct1));
NSLog(@"Struct2占用内存大小:%lu",sizeof(Struct2));
输出结果:
Struct1占用内存大小:24
Struct2占用内存大小:16
同样的变量,只是顺序不同,占用的内存不一样,这和内存对齐有关
- 首先看Struct1和Struct2,先找到长度最大的变量,就是double占8字节,这样可以确定结构体是以8字节对齐的(内存大小是8的倍数)
先分析Struct1
- a占1字节放在0号位置上
- b占8字节但是1号位置不是8的整倍数,最近的整倍数位置是8,因此b放在8号位置上
- c占2字节放在16号位置上
- d占4字节但是18号位置不是4的整倍数,最近的整倍数位置是20,因此d放在20号位置上
同理分析Struct2
扩展
struct Struct3 {
int a; //4字节
char b; //1字节
int c; //4字节
} Struct3;
struct Struct4 {
struct Struct3 str3;
struct Struct1 str1;
} Struct4;
NSLog(@"Struct4占用内存大小:%lu",sizeof(Struct4));
输出
Struct4占用内存大小:40
分析:
Struct3最大长度是4,Struct3整体是4字节对齐
Struct1最大长度是8,Struct1整体是8字节对齐
Struct1是8字节对齐的,12不是8的整数倍,因此Struct1从16号位开始
这里介绍获取内存的三种方式:
- sizeof
- class_getInstanceSize
- malloc_size
sizeof
- sizeof是一个操作符,不是函数
- 我们一般用sizeof计算内存大小时,传入的主要对象是数据类型,这个在编译器的编译阶段(即编译时)就会确定大小而不是在运行时确定。
- sizeof最终得到的结果是该数据类型占用空间的大小
class_getInstanceSize
- 用于获取类的实例对象所占用的内存大小,并返回具体的字节数,其本质就是获取实例对象中成员变量的内存大小
malloc_size
- 获取系统实际分配的内存大小