阅读 873

iOS 结构体内存对齐

什么是内存对齐

还是用一个例子带出这个问题,看下面的小程序,理论上,32位系统下,int占4byte,char占一个byte,那么将它们放到一个结构体中应该占4+1=5byte;但是实际上,通过运行程序得到的结果是8 byte,这就是内存对齐所导致的。

> #include<stdio.h>
> struct{
>     int x;
>     char y;
> }s;
> 
> int main()
> {
>     printf("%d\n",sizeof(s);  // 输出8
>     return 0;
> }
复制代码

现代计算机中内存空间都是按照 byte 划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐。

为什么要进行内存对齐

内存是以字节为基本单位,cpu在存取数据时,是以块为单位存取,并不是以字节为单位存取。频繁存取未对齐的数据,会极大降低cpu的性能。字节对齐后,会减低cpu的存取次数,这种以空间换时间的做法目的降低cpu的开销。 cpu存取是以块为单位,存取未对齐的数据可能开始在上一个内存块,结束在另一个内存块。这样中间可能要经过复杂运算在合并在一起,降低了效率。字节对齐后,提高了cpu的访问速率。

假如没有内存对齐机制,数据可以任意存放,现在一个int变量存放在从地址1开始的联系四个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),然后从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器.这需要做很多工作.

截屏2021-06-13 下午2.44.40.png

现在有了内存对齐的,int类型数据只能存放在按照对齐规则的内存中,比如说0地址开始的内存。那么现在该处理器在取数据时一次性就能将数据读出来了,而且不需要做额外的操作,提高了效率

结构体内存对齐原则

1.数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方(即首地址的位置),以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。

2.结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char、int 、double等元素,那b应该从8的整数倍开始存储.)

3.收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬。

我们通过几个例子来了解一下

struct XJStruct1 {
    double a;       // 8    [0 7]
    char b;         // 1    [8]
    int c;          // 4    (9 10 11 [12 13 14 15]
    short d;        // 2    [16 17] 24
}struct1;

![截屏2021-06-13 下午3.12.33.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/569549afac0d471c968ac05de36c29b2~tplv-k3u1fbpfcp-watermark.image)
struct XJStruct2 {
    double a;       // 8    [0 7]
    int b;          // 4    [8 9 10 11]
    char c;         // 1    [12]
    short d;        // 2    (13 [14 15] 16
}struct2;

复制代码

在这里整理些基本数据类型在不同系统下的字节大小,方便大家查看。

27b1891427a4494baaec430175393223_tplv-k3u1fbpfcp-watermark.jpg

乍一看这两个结构体是几乎一模模一样样的,所以感觉上觉得两个结构体所占用的内存也是一样的,其实不然,成员变量的顺序也会影响到结构体内存的大小,我们跑一下验证一下

截屏2021-06-13 下午3.02.11.png

不出所料 打印的时候 字节是不一样的 这里面问题就出在了b 与c 的类型与顺序上。double的大小为8个字节,按照规则1, a成员变量将会占据前首地址开始的 8个字节。也就是0x00到0x07的地址。char的大小为1,按照规则1,内存中的第9为是1的倍数,占据一个字节0x08,单数成员变量c,为int类型,大小为4,内存中的第10为并不是4的倍数,所以并不能满足规则1。因此,成员变量b要补3个位置,即占据0x08到0x11的地址单元,成员变量c从0x12到0x15开始占据4个内存单元。同理,按照规则1可以得出成员变量d占据0x16到0x08的内存单元,总共占据18个字节。最后,我们进行收尾工作:结构体的总大小,必须是其内部最大成员的整数倍。内部最大成员为double 8个字节,可以计算出结构的总大小为24;同理可得第二个结构体的内存为16.

如果结构体中存在结构体成员变量呢?

按照规则1,可以得出前5个变量占据24个内存单元,对于结构体成员str,其内部最大成员为double 8个字节,而24为8的倍数,符合规则1, str占据后面24个字节单元。此时,结构体总共占据48个内存单元,按照规则3 XJStruct2的最大成员是 XJStruct1 str 24个字节,符合规则。 下面我么用代码验证下结果

截屏2021-06-13 下午3.25.02.png

总结

总得来说内存对齐是指定了一套规则,以提高访问效率的目的来合理利用内存空间。系统通过一定的代码填充来达到在一个指令完成每个成员的访问,不再重复合拼和拆分。从而达到空间换时间的效果,提高效率。

文章分类
iOS
文章标签