关于结构体内存对齐以及大小端

一直以来,结构体内存对齐都是大家讨论的热门话题,特别是对于初学者,总是会感觉理不清楚,本人最开始也是死记硬背,但是可想而知,过一段时间用的时候就会混淆。这几天又看了几篇关于内存对齐的文章,感觉略有所获,这里也分享下我的心得,同时也让自己加深理解。其实要搞清楚内存对齐的问题,有两个概念要弄清楚。一个就是硬件本身的内存分布,另一个就是结构体变量的内存分布;至于硬件本身的内存分布,我们可以想象成为一格一格的,所有的数据都是存放在一格或者几格中,在寻址读取数据的时候,每一次读取N格,因此为了提高内存的访问速度,避免数据的存储起始地址在某一格内存的中端,这也是结构体内存对齐的由来。因为虽然编译器会自动做对齐的动作,但是它也不是万能的,人为的控制一下有助于优化提高。扯远了,来看下结构体的内存对齐,实际上,我们只要把握住对齐的要点,那就是一定要保证每一个结构体成员变量的起始地址是min(这个变量长度,编译器对齐字节)的整数倍,结构体整体的长度是min(所有成员长度的最大值,编译器对齐字节)的整数倍。对于嵌套结构体,它的对齐因子并不是这个嵌套结构体的总长,而是这个结构体的对齐因子。哎,文笔太差,直接上两个例子吧,希望大家谅解。

struct A

{

   char c;

   int b;

};

struct B

{

    char b;

    A a;

    short s;

};

在4字节对齐的情况下,sizeof(A)=8,结构体中c的地址位结构体A的首地址,b是int型,长度为4,因此它的存储起始地址必须是min(4,4)=4的整数倍,这个时候就可以把硬件的内存看成是4个字节一格,c在一格,剩余3个字节不足以存储长度位4的成员b,因此直接补齐3个0,长度就是1+3+4=8了,这个时候8刚好也是4的整数倍,所以不需要补齐。

sizeof(B)=1+3+1+3+4+2+2 = 16,最后那一个2是为了结构体整体大小是4的整数倍而补齐的。

下面继续讨论一点进阶的内容,结构体中的变量从前到后是顺序从低地址到高地址存储的,那来看下面的结果:

B obj;

(unsigned int)&obj.a-(unsigned int)&obj.b = 4,这里的4也就是考察的是填充的长度,因为结构体B的对齐因子是4,如果obj.b的地址是0的话,那么obj.a的地址就是4,所以结果就是4.


ps,所谓的结构体对齐因子就是结构体中长度最大的成员变量的长度与编译器对齐字节的最小值。


哎,越说越不清楚,反正大家记住,要想正确的计算结构体对齐后的长度,首先把每个成员的对齐因子算出来,然后每一个成员的起始地址就是这个因子的整数倍,最后加成出来的总长度再补齐成这个结构体因子的整数倍。

再说一点结合大小端,结构体赋值的知识,一般Windows系统都是小端存储,所谓小端就是从左到右,地址越来越小,比如一般0x12345678,那么0x78就是低地址了,那看下面:

这里为了需要,将A的结构体定义修改为:

struct A

{

   char c;

   short b;

};


unsigned long long u = 0x12345678;

B* b = (B*)&u;这样一来,b的首地址就是u的起始地址了,加上小端存储,所以b->b=0x78,结构体B的成员b后面有一个字节的补齐,因此0x56被补齐字节占据,b->a.c=0x34,b->a.b=0x12;

你可能感兴趣的:(关于结构体内存对齐以及大小端)