对于底层的研究,我们都会关心内存的分配,今天我们先探索一下结构体内存大小的分配原则
准备工作
我们先看一下不同类型的数据所占内存大小
OC基础数据类型
NSLog(@"OC基础数据类型:\n");
NSLog(@"BOOL\t%lu\n", sizeof(BOOL));
NSLog(@"int8_t\t%lu\n", sizeof(int8_t));
NSLog(@"Boolean\t%lu\n", sizeof(Boolean));
NSLog(@"int16_t\t%lu\n", sizeof(int16_t));
NSLog(@"unichar\t%lu\n", sizeof(unichar));
NSLog(@"NSInteger\t%lu\n", sizeof(NSInteger));
NSLog(@"NSUInteger\t%lu\n", sizeof(NSUInteger));
NSLog(@"int64_t\t%lu\n", sizeof(int64_t));
NSLog(@"CGFloat\t%lu\n", sizeof(CGFloat));
打印结果
OC基础数据类型:
BOOL 1
int8_t 1
Boolean 1
int16_t 2
unichar 2
NSInteger 8
NSUInteger 8
int64_t 8
CGFloat 8
C基础数据类型:
printf("C基础数据类型:\n");
printf("bool\t%lu\n", sizeof(bool));
printf("char\t%lu\n", sizeof(char));
printf("unsigned char\t%lu\n", sizeof(unsigned char));
printf("short\t%lu\n", sizeof(short));
printf("unsigned short\t%lu\n", sizeof(unsigned short));
printf("int\t\t%lu\n", sizeof(int));
printf("unsigned int\t\t%lu\n", sizeof(unsigned int));
printf("long\t%lu\n", sizeof(long));
printf("unsigned long\t%lu\n", sizeof(unsigned long));
printf("long long\t%lu\n", sizeof(long long));
printf("float\t%lu\n", sizeof(float));
printf("double\t%lu\n", sizeof(double));
打印结果
C基础数据类型:
bool 1
char 1
unsigned char 1
short 2
unsigned short 2
int 4
int32_t 4
unsigned int 4
long 8
unsigned long 8
long long 8
float 4
double 8
C和OC基础数据类型大小对照表
写个🌰,先看一下大小
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(@"struct1 size %lu \n struct2 size %lu",sizeof(struct1),sizeof(struct2));
打印结果
struct1 size 24
struct2 size 16
对比两个结构体,有四个相同类型的基础数据,只是顺序不同,为什么得到的大小却不同呢
内存对齐原则
-
数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在
offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。min(当前开始的位mn)m=9n=4 9 10 11 12 -
结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(
struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储.) -
收尾工作:结构体的总大小,也就是
sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬。
根据对齐原则,分析一波
struct1和struct2里面没有结构体,根据对齐原则的第一条和第三条,分析一下struct1和struct2
struct1
根据对齐原则第一条 起始位置要从该成员大小的整数倍开始
double adouble是8字节 [0 7]
char bchar是1字节8能被1整除 [8]
int cint是4字节 9不能被4整除 所以从12开始 (9 10 11 [12 13 14 15]
short dshort是2字节,16能被2整除,[16 17]
根据对齐原则第三条 结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬。
struct1 最大的是double 8字节 ,所以sizeof是24
struct2
根据对齐原则第一条 起始位置要从该成员大小的整数倍开始
double adouble是8字节 [0 7]
int bint是4字节 8能被4整除[8 11]
char cchar是1字节12能被1整除 [12]
short dshort是2字节,13不能被2整除,所以从14开始 (13 [14 15]
根据对齐原则第三条 结构体的总大小,也就是
sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬。
struct2 最大的是double 8字节 ,所以sizeof是比15大的8的整数倍,也就是16
写一个新的结构体自己分析
struct Struct3 {
double a;
int b;
char c;
short d;
char e;
}struct3;
根据对齐原则第一条 起始位置要从该成员大小的整数倍开始
double adouble是8字节 [0 7]
int bint是4字节 8能被4整除[8 11]
char cchar是1字节12能被1整除 [12]
short dshort是2字节,13不能被2整除,所以从14开始 (13 [14 15]
char echar是1字节,16能被2整除,所以从16开始 [16 17]
根据对齐原则第三条 结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬。
struct3 最大的是double 8字节 ,这个正好到16,那么struct3的大小是不是16呢
打印一下
struct3 size 24
为什么是24呢,因为offset是从0开始的,所以0-16是17个字节,因此size是24。
结构体嵌套结构体
struct Struct4 {
double a;
int b;
char c;
short d;
int e;
struct Struct2 str;
}struct4;
double adouble是8字节 [0 7]
int bint是4字节 8能被4整除[8 11]
char cchar是1字节12能被1整除 [12]
short dshort是2字节,13不能被2整除,所以从14开始 (13 [14 15]
int eint是4字节 16能被4整除 [16 17 18 19]
struct Struct2 str 是16字节 根据字节对齐第二条 结构体成员要从其内部最大元素大小的整数倍地址开始存储 (20 21 22 23 [24 39]
根据对齐原则第三条 结构体的总大小,也就是
sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬。
struct4 最大的是struct2 16字节 ,那么struct4的大小是不是48呢
打印一下
struct4 size 40
补充第三条,收尾工作:结构体的总大小,也就是
sizeof的结果,必须是其内部最大成员(非结构体成员,如果成员是结构体,看结构体里的最大成员)的整数倍,不足的要补⻬。(struct a里存有struct b,b里有char,int等元素,struct a里有char,int,short,double等元素struct a和struct b中最大的是double8字节,所以sizeof是8的倍数。)
验证
struct Struct5 {
int a;
int b;
char c;
short d;
}struct5;
struct Struct6 {
int a;
int b;
char c;
short d;
int e;
struct Struct5 str;
}struct6;
struct Struct7 {
double a;
char b;
short c;
int d;
struct Struct5 str;
}struct7;
根据分析,struct5大小是12,
- 分析
struct6
int aint是4字节 [0 3]
int bint是4字节 4能被4整除[4 7]
char cchar是1字节 8能被1整除 [8]
short dshort是2字节,9不能被2整除,所以从10开始 (9 [10 11]
int eint是4字节 12能被4整除,[12 15]
struct Struct5 str12字节 但struct5最大元素是4字节 16能被4整除 [16 27]
struct5中的元素和struct6最大元素是4字节,所以sizeof是28
- 分析
struct7
int aint是4字节 [0 3]
int bint是4字节 4能被4整除[4 7]
char cchar是1字节 8能被1整除 [8]
short dshort是2字节,9不能被2整除,所以从10开始 (9 [10 11]
int eint是4字节 12能被4整除,[12 15]
struct Struct5 str12字节 但struct5最大元素是4字节 16能被4整除 [16 27]
struct5中的元素和struct7最大元素是8字节,所以sizeof是32
打印一下看结果:
struct5 size 12
struct6 size 28
struct7 size 32
总结
- 数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。min(当前开始的位mn)m=9n=4 9 10 11 12
- 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(
struct a里存有struct b,b里有char,int,double等元素,那b应该从struct b中最大元素double8的整数倍开始存储.)- 收尾工作:结构体的总大小,也就是
sizeof的结果,必须是其内部最大成员(非结构体成员,如果成员是结构体,看结构体里的最大成员)的整数倍,不足的要补⻬。(struct a里存有struct b,b里有char,int等元素,struct a里有char,int,short,double等元素。struct a和struct b中最大的是double8字节,所以sizeof是8的倍数。)