问题: 如下两个结构体,只是成员位置不同,占用内存大小是否相同呢?
struct LCStruct1 {
double a; // 8字节
char b; // 1字节
int c; // 4字节
short d; // 2字节
}struct1;
struct LCStruct2 {
double a; // 8字节
int c; // 4字节
char b; // 1字节
short d; // 2字节
}struct2;
NSLog(@"struct1 sizeof = %lu; struct2 sizeof = %lu",sizeof(struct1),sizeof(struct2));
复制代码
这个题目考查的就是内存对齐的知识点,看完这篇文章你就知道这道题的答案是什么了。
一、什么是内存对齐
内存对齐(Memory alignment),也叫字节对齐。
现代计算机中内存空间都是按照 byte 划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
举一个简单的例子, 64位系统,int所占内存空间为 4 bytes,char为 1 byte。如果把它们放在一个结构体中,则所占的内存空间应该是 4 + 1 = 5 bytes 。而事实上,在Xcode环境,sizeof 操作的结果都是 8 bytes:
二、为什么要进行内存对齐
之所以要内存对齐,有两方面的原因:
-
平台原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如,有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐。
-
性能原因:内存对齐可以提高存取效率。比如,有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。
三、对齐的规则
1. 数据成员
对⻬规则:结构(struct)
(或联合(union)
)的数据成员,第⼀个数据成员放在offset为0的地⽅
,以后每个数据成员存储的起始位置要从该成员⼤⼩或者成员的⼦成员⼤⼩
(只要该成员有⼦成员,⽐如说是数组,结构体等)的整数倍开始
(⽐如int为4字节,则要从4的整数倍地址开始存储。
2. 结构体
作为成员:如果⼀个结构⾥有某些结构体成员
,则结构体成员要从其内部最⼤元素⼤⼩的整数倍
地址开始存储.(比如:struct a⾥存有struct b, b⾥有char,int ,double等元素,那b应该从8的整数倍开始存储.)
3. 结构体的总⼤⼩,也就是sizeof的结果,必须是其内部最⼤成员的整数倍.不⾜的要补⻬
。
4. 如程序中有#pragma pack(n)预编译指令
,则所有成员对齐以n字节
为准(即偏移量是n的整数倍),不再考虑当前类型
以及结构体内最大类型
四、实践与验证
上面两个结构体对象student1
和student2
成员变量的类型相同,只是顺序不一样,最终占用的内存空间大小(单位:字节)却不一样.sizeof(struct1) = 24
, sizeof(struct2) = 16
以上面结构体struct1
为例,
第1个成员a是double
类型,占用8个字节
空间,偏移量为0
第2个成员b
是char
类型,占用1个字节
空间,按照规则1
,b
的偏移量必须是char
类型所占大小的的整数倍,当前偏移量为8
,是其类型大小的整数倍,所以b
紧跟在a
的后面.
第3个成员变量c
的类型是int
类型,占用4个字节
空间,按照规则1
,c
的偏移量必须是int
类型的整数倍,所以编译器会在b变量
后面插入3字节缓冲区,保证此时c
的偏移量(12字节)是c类型的整数倍(当前恰好是3倍),
第4个成员d
为short
类型,占用2个字节
空间, 此时d的偏移量正好是16个字节,已经是short
类型大小的整数倍,故c
与d
之间不用填充缓冲字节。
但这时,结构体struct1
的大小为18个字节,按照规则3
,结构体struct1大小必须是其最大成员类型double
的整数倍,所以需要在18个字节的基础上再填充6个字节,保证最后结构体大小为24,以符合规则3
.
同理,可以得到结构体struct2
的内存分布:
进阶
未完待续。。。