1.什么是结构体内存对齐
结构体内存对齐是指在编程语言中,为了提高内存访问效率和性能,将结构体的成员按照特定规则进行排列,保证每个成员在内存中的起始地址符合特定的对齐要求。
2.为什么要结构体内存对齐
网上的文章都是说这两个原因:
1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
(1)如果不进行内存对齐
如果不进行内存对齐那么读取结构体里的某个成员可能需要读取两次才能读取出来,下面举一个例子:
struct A
{
char a;
int b;
short c;
}
对于这个结构体A假设不进行内存对齐,现要读取结构体中的成员b,并根据#pragma pack(4)规则方式读取,那么计算机访问成员b的过程如下:
要访问结构体成员中int类型变量b时我们就要一次读取四个字节,但是只能从存储边界开始读取,又因为变量b没有对齐在存储边界,所以这里我们读取时就需要读取两次才可以将变量b从内存中读取出来,而且还要对读取出来的内容进行剔除,这就需要更多的时间。
这里 根据#pragma pack(4)规则,其存储边界是这样的:
(2)如果进行内存对齐
如果进行内存对齐,那么读取结构体中的成员b,并根据#pragma pack(4)规则方式读取,那么计算机访问成员b的过程如下:
一次就读取成员变量b。
3.结构体大小,位域大小计算
(1)结构体大小计算
结构体大小,位域大小计算这里不说哪些对齐规则了,直接说我的计算方法,网上很多文章说的对齐规则我也记不住。直接上例子。
struct A
{
char a;
int b;
short c;
}
如果根据#pragma pack(1)对齐规则,结构体不需要补齐,读取成员变量是一个字节一个字节地读取,这个结构体大小是1+4+2=7
如果根据#pragma pack(4)对齐规则,计算方法看下图:
从第一个成员开始找,直至找到类型大小为最大的(注意一定是按照升序中最大的,如果第一个比二个大,那就这一个作为一段成员),然后从第一个成员到这个最大的成员这一段按照“#pragma pack指定的和这段类型最大的比较, 选择小的作为对齐规则”对齐计算。
最后一段的是一个成员的话,直接算大小,所以8+2=10,然后整个结构体最大类型为int,4个字节,4 与#pragma pack(4)比较,取小的也是4。10不能整除4,所以要补齐到12。因此这个结构体大小是12。
如果成员变量a,b位置调换的话,分成以下两段计算:
第一段4 与#pragma pack(4)比较,取小的也是4,所以占4字节
第二段最大为2,2 与#pragma pack(4)比较,取小的是2,这段2字节对齐,a后面补一个字节。一次读2字节,要读两次才能读完这一段,读两次是4字节,所以占4字节
因此4+4=8,8能整除4,所以占8字节。
第二个例子:
所以第二个例子大小为36+4+2=42 然后该结构体成员类型最大为weight,8字节。但#pragma pack(4)指定4字节,取小的,因为42/4不能整除,补齐到能整除,所以大小为44。
如果这个结构体按照 #pragma pack(8),计算方式如下:
所以按照 #pragma pack(8),大小为40+4+2=46,#pragma pack(8)指定8字节,等于该结构体最大类型的大小,46不能整除8,补齐到48. 所以大小为48。
(2)位域大小计算
什么是位域,网上很多文章介绍这里不详细说,直接上例子
位域对齐方法和上面结构体一样。这个例子大小位4+8=12字节。