8月のアップデートチャレンジに参加して2日目です。イベントの詳細については、8月のアップデートチャレンジをご覧ください。
メモリアラインメントの原則
1.データメンバーの配置ルール
構造体(struct
)または和集合(union
)のデータメンバーの場合、最初のデータメンバーはオフセットが0の場所に配置され、後続の各データメンバーの開始位置は、メンバーのサイズまたはサブのサイズから格納されます。 -メンバーのメンバー(メンバーに配列、構造体などのサブメンバーがある場合)は、整数の倍数(int
4バイトなど、4の整数倍から格納する必要があります)から始まります。
2.メンバーとしての構造
構造体にいくつかの構造体メンバーがある場合、構造体メンバーは、構造体の最大要素のサイズの整数倍から格納する必要があります。(structaにはstructbが含まれ、bにはchar、int、double、およびその他の要素が含まれます。その場合、bは8の整数倍から格納する必要があります)。
3.最初の仕事
sizeofの結果である構造体の合計サイズは、最大メンバーの整数倍でなければなりません。足りないものを補ってください。
プリミティブデータ型のメモリサイズ
C | OC | 32ビット | 64ビット |
---|---|---|---|
ブール | BOOL(64ビット) | 1 | 1 |
署名された文字 | (__signed char)int8_t、BOOL(32ビット) | 1 | 1 |
unsigned char | ブール値 | 1 | 1 |
短い | int16_t | 2 | 2 |
unsigned short | ユニチャー | 2 | 2 |
int、int32_t | NSInteger(32ビット)、boolean_t(32ビット) | 4 | 4 |
unsigned int | boolean_t(64ビット)、NSUInteger(32ビット) | 4 | 4 |
長いです | NSInteger(64ビット) | 4 | 8 |
unsigned long | NSUInteger(64ビット) | 4 | 8 |
長い長い | int64_t | 8 | 8 |
浮く | CGFloat(32ビット) | 4 | 4 |
ダブル | CGFloat(64ビット) | 8 | 8 |
メモリサイズを取得する方法
1. sizeof(expression-or-type)
sizeof()
是C/C++
中的关键字,它是一个运算符,不是函数。作用是取得一个对象(数据类型或者数据对象)的长度(即占用内存的大小,以byte
为单位,返回size_t
)。基本数据类型(int
、double
等)的大小与系统相关。结构体涉及字节对齐。
示例:
struct Stu {
char c;
int i;
double d;
};
void test() {
//基本数据类型
int age = 18;
size_t sizeAge1 = sizeof(age);
size_t sizeAge2 = sizeof age;
size_t sizeAge3 = sizeof(int);
NSLog(@"age size: %zu, %zu, %zu",sizeAge1,sizeAge2,sizeAge3);
//结构体
struct Stu s;
s.c = 'c';
s.i = 18;
s.d = 180.0;
size_t sizeS1 = sizeof(s);
size_t sizeS2 = sizeof s;
size_t sizeS3 = sizeof(struct Stu);
NSLog(@"s size: %zu, %zu, %zu",sizeS1,sizeS2,sizeS3);
//指针
NSObject *obj = [NSObject alloc];
size_t sizeObj1 = sizeof(obj);
size_t sizeObj2 = sizeof obj;
size_t sizeObj3 = sizeof(NSObject *);
NSLog(@"obj size: %zu, %zu, %zu",sizeObj1,sizeObj2,sizeObj3);
}
复制代码
输出:
age size: 4, 4, 4
s size: 16, 16, 16
obj size: 8, 8, 8
复制代码
- 通过类型和实例都可以获取内存大小,这也说明开辟的内存大小在类型确定后就已经确定了。
sizeof
是运算符不是函数。3种语法形式都可以,需要注意的是通过类型获取的方式必须在()
中。
2. class_getInstanceSize
这个函数是rutime
提供的获取类的实例锁占用的内存大小。大小至于成员变量有关。获取的是实际占用的空间(8字节对齐
)。
3. malloc_size
malloc_size
就是alloc
中实际开辟的空间。
案例份分析
1.有如下结构体Struct1
和Struct2
分别占用多大内存?
struct Struct1 {
double a; // [0,7]
char b; // [8]
int c; // 根据第一准则要从4的倍数开始,所以[12,13,14,15]。跳过9,10,11
short d; //[16,17]
}struct1;
//根据第三准则总大小要是8的倍数,那就要分配24字节。
struct Struct2 {
double a; //[0,7]
int b; //[8,11]
char c; //[12]
short d; //根据准则1跳过13,从14开始 [14,15]
}struct2;
//这里0~15大小本来就为16了,所以不需要补齐了。
复制代码
验证:
NSLog(@"struct1 size :%zu\nstruct2 size:%zu",sizeof(struct1),sizeof(struct2));
复制代码
输出:
struct1 size :24
struct2 size:16
复制代码
- 结构体中数据类型顺序不一致占用的内存大小可能不一致。
- 大小计算从
0
开始,Struct2
并没有进行第三原则补齐。
2. 增加一个Struct3
中有结构体嵌套,那么占用大小是多少?
struct Struct3 {
double a; //[0,7]
int b; //[8,11]
char c; //[12]
short d; //跳过13 [14,15]
int e; // [16,19]
struct Struct1 str; //根据准则2,Struct1最大元素为`double`类型,所以从24开始。根据`Struct1`分配的时候24个字节,所以str为[24,47]
}struct3;
//所以Struct3占用内存大小为48字节。
复制代码
验证:
NSLog(@"struct3 size :%zu",sizeof(struct3));
struct3 size :48
复制代码
在这里有个疑问,准则3
是先作用在Struct1
再作用Struct3
还是最后直接作用在Struct3
?不妨验证一下:
struct Struct4 {
struct Struct1 str;
char c;
}struct4;
复制代码
Struct1
本身占用18字节
,补齐后占用24字节
。如果Struct4
最终占用32字节那么就是第一种情况,张永24 字节则是第二种情况。
NSLog(@"struct4 size :%zu",sizeof(struct4));
struct4 size :32
复制代码
这也就验证了猜想,结构体嵌套从内部开始补齐。这也符合常理。
3. 修改结构体如下:
struct S1 {
int a; // 4 [0,3]
char b;// 1 [4]
short c; // 2 [6,7]
}; // 0~7 8字节
struct S2 {
double a; // 8 [0,7]
char b; // 1 [8]
struct S1 s1; // 8 [12,19] 按s1自身中存的最大a的4字节的倍数对齐
bool c; // 1 [20]
};
//0~20一共21个字节,按最大的8字节对齐。应该24字节。
复制代码
struct S2 s2;
NSLog(@"size :%zu",sizeof(s2));
复制代码
这个时候s2
大小为多少?
分析:
-
S1:
int a
4
から1バイトを占有します[0~3]
。char b
1
バイトを占有します[4]
。short c
1バイトを使用し、バイトに揃える2
必要があるため、スキップしてください2
5
[6~7]
S1
全体0~7
を埋める必要はありません。バイト8
。
-
S2:
double a
バイト8
、[0~7]
。char b
バイト1
、[8]
。struct S1 s1
バイト8
。S1
内部の最大要素はそうなので、位置合わせint a
する必要があります。4
[12~19]
bool c
バイト1
、[20]
。S2
全体は0~21
合計21
バイトからのものであり、中央S2
の最大の要素でdouble a
埋める必要があります。24
したがって、バイトである必要があります。
出力:
size :24
复制代码