探索结构体的内存对齐

2021-06-09  本文已影响0人  CHEN_JHiiiii

 先上两个简单的结构体,思考:这两个结构体大小是不是一样?

附上:各数据类型的字节长度

运行以下代码,查看打印结果:

这个结果有没有在意料之中,以64位系统为例,struct1存取的字节数: double为8 、char为1、int为4、short为2,这就奇怪了,为什么读取内存不是按字节大小一个个读呢?这样就是8 + 1 + 4 + 2 = 15,这样看来不是更加省内存吗?通过查阅资料发现,处理器一般会以2字节,4字节,8字节,16字节甚至32字节为单位来存取内存。以这个思路重新计算一下,double以8字节存取,char以2字节存取,int以4字节存,short以2字节存,这样就是8 + 2 + 4 +2 = 16,并不是24,反而是struct2的大小。有没有可能计算机有一种策略,为了不在各种字节存取反复横跳,例如一会4字节存取,一会2字节存取,如果这样做会导致计算机开销巨大,是不是他就会定下一种策略,统一一个字节去存取,比如统一使用8字节存取,那么4个类型就是8 * 4 = 32,这里似乎又超过24,那么很可能的是计算机既做到了不用反复横跳的存取,又实现了内存的节省,究竟是怎么回事呢?

猜测1:结构体是不是将第一个成员变量作为首地址先存取了,然后后续的成员,再按谁最大,就以多少字节存取?那么来算一下:

double : 8   ,后面3个成员分别是 char int short , int占4个字节,所以后面以4字节读取,则通过计算得出

8 + 4 + 4 + 4 = 20 ,还是不对。

猜测2:第一个成员变量作为首地址先存取,后续成员的存取是不是和当前读取到的位置存在倍数关系,再进行一次计算

double   : 8       【0 ~  7】

char       : 1      【8】 有倍数关系

int          : 4            (9 , 10 , 11 ) 无倍数关系  【12】有倍数关系,从这开始读4位就是【12 13 14 15】

short     :  2         【16】有倍数关系 ,那就是【16,17】

竟然是奇数!好像还是不对,难道他的总大小也会有倍数关系吗?看结果 24,确实是和里面的任意一个成员都有倍数关系,那么我是不是可以合理猜测,当内存计算出的结果和里面的成员变量没有倍数关系时,他会自动补齐后续内存以达成倍数关系。从17 到 24之间 ,也只有24和所有成员变量有倍数关系,只要总大小和里面最大的成员成倍数关系,那么与其他的都是倍数关系,这也是为什么不是20,而是24.以这种思路计算struct2

double  : 8    【0 ~  7】

int         : 4     【8 9 10 11】

char      : 1     【12】

short     : 2      【 14  15 】

这里只读取到15,和 8 并不存在倍数关系,往后再读一位 那么就是 16

我们再写多几个结构体试试

计算struct 4:

 char      : 1    【0】

 char      : 1    【1】

 short     : 2    【 2  3 】

 int         : 4    【4 5 6 7】

最后补齐结果为 : 8 ,是4的倍数

计算struct 5

int           4      【0 1 2 3】

double    8      【8 9 10 11 12 13 14 15】

long       8       【16 17 18 19 20 21 22 23】

short      2       【24 25】

最后同样要补齐结果为: 32  , 是 8的倍数

打印验证:

继续往下研究,如果结构体里也有结构体,如下图,结构体3包含了结构体5

 我们依旧按照前面的方法计算前5个成员

double      8       [ 0  ~ 7]

int             4       [8 ~ 11]

char          1       [12]

short          2      [14 15]

int              4      [16 ~ 19]

那么问题来了,我们已知struct5是32,那他是从32开始,然后直接32 + 32等于64呢?还是需要从结构体5内部开始存呢?如果从结构体5内部的话,那么接着上面计算结构体5内部:

int           4    [20 ~ 23]

double    8   [24 ~ 31]

long        8   [32 ~ 39]

short       2   [40 41]

最后补齐倍数那就是8的倍数,往后存取就是48,

打印结果:

 结果真的打脸,既不是64也不是48,如果计算内部时,不是以第一个成员的倍数开始,而是以最大的成员的倍数开始,  结果又会怎样呢?接着计算:

int           4    [24 ~ 27]

double    8   [32 ~ 39]

long        8   [40 ~ 47]

short       2   [48 49]

最后补齐:56

 我的天,还真的是这样 ?为了进一步验证,我把struct5换成struct4,计算出的结果是32,大家可以尝试一下

结论:

关于结构体内存对齐,我得出几点原则:

1.从第一个成员开始,首地址为0,后续成员的首地址需要和他们的大小成倍数关系。

2. 最终大小如果和结构体里最大的成员无法成倍数关系,必须从当前地址继续往后偏移,直到地址和该成员成倍数关系,那么当前的地址就是结构体最终大小。

3.如果是结构体嵌套结构体,计算大小过程并不是单一的把结构体大小加上其他成员变量,而是以结构体中内部最大的成员来找到和该成员对应的地址,然后再根据 1,2点进行运算,最终得出结果。

探索收获:
为什么要进行结构体对齐?

从文章开头举的两个结构体例子中,我们可以知道,即使结构体里面的内容相同,但是大小确不是一样。通过探索结构体对齐,我明白在设计结构体中,一个好的结构体,往往能做到存取过程中,既节省空间,又能节省时间,比如例子中结构体2就比结构体1节省了8个字节,在文章上面计算struct1过程中,我们可以在计算中知道,结构体1,既要空出多余的空间,又要为了节省存取时间去补上了多余的空间,比如17到24这部分空间就是浪费了,而结构体2就没有这方面问题,从对比发现,似乎在设计结构体时,成员较小的尽量放到一起的话,会更省空间。虽然结构体内存对齐是为了用空间换取时间,但是我们作为开发者,也是可以通过巧妙的设计去帮助结构体节省空间。

上一篇下一篇

猜你喜欢

热点阅读