结构体字节对齐

1,172 阅读5分钟

结构体字节对齐

什么是对齐?为什么要对齐?

  • 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
  • 如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。

#pragma pack()语法

  • #pragma pack 的主要作用就是改变编译器的内存对齐方式,这个指令在网络报文的处理中有着重要的作用,#pragma pack(n)是他最基本的用法,其作用是改变编译器的对齐方式, 不使用这条指令的情况下,编译器默认采取#pragma pack(8)也就是8字节的默认对齐方式,n值可以取(1, 2, 4, 8, 16) 中任意一值。
  • 我们常说编译器默认8字节对齐我们怎么知道的呢?#pragma pack(show)显示当前内存对齐的字节数。显示当前packing aligment的字节数,以warning message的形式显示。 image.png

结构体对齐的规则

原则1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。(即pack指定的值和成员自身比较,小的那个整数倍对齐)

原则2:结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。(即pack指定的值和最大成员自身比较,小的那个整数倍对齐)

原则3:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。

结构体对齐验证

#pragma pack(2)
struct Struct1{
    double a; //0-7
    char b;   //8
    int c;    //起始min(2,4)=2,所以从2的倍数开始 10 11 12 13
    short d;  //14 15
} structOne;  //min(2,8)=2, 2的倍数  16
#pragma pack()

struct Struct2{
    double a; //0-7
    char b;   //8
    int c;    //起始min(8,4)=4,所以从4的倍数开始 12 13 14 15
    short d;  //16 17
} structTwo;  //min(8,8)=8, 8的倍数  24  

运行结果:

image.png 可以看出此时structOne和structTwo,内部成员变量顺序完全一致,指定了#pragma pack()之后sizeof得到的结果完全不同。

  • 那么如果编译器默认8字节对齐时,成员相同顺序不同,结构体的字节又是多少呢?
#pragma pack(show)
struct Struct1{
    double a; //0-7
    char b;   //8
    int c;    //起始min(8,4)=4,所以从4的倍数开始 12 13 14 15
    short d;  //16 17
} structOne;  //min(8,8)=8, 8的倍数 24;  

struct Struct2{
    double a; //0-7
    int c;    //起始min(8,4)=4,所以从4的倍数开始 8 9 10 11
    char b;   //12
    short d;  //14 15
} structTwo;  //min(8,8)=8, 8的倍数  16

运行结果

image.png 可以看出结构体成员顺序不同,虽然pragma pack相同,sizeof得到的字节数不同。

  • 如果将structOne中的double a拆解成两个int成员,structOne的字节数应是20了

image.png

这是因为结构体整体对齐,是按照最大成员变量int长度的和pack默认的8中最小比较是4,4的最小倍数是20;

  • 结构体嵌套

image.png

image.png

  • 对比两张图明显发现,当出现结构体嵌套时,外层结构体整体对齐时,取得是内外结构体中最大成员变量的倍数

总结

结构体的对齐显示成员对齐,然后是结构体的整体对齐,根据内存对齐原则,来对齐内存。

1:根据数据成员对齐:

根据#pragma pack(x) 根据x这个数值和依次排列的数据成员自身的长度的数做比较,那个小用哪个。

这样就排出来了每一个数据所占的地址及位置。是依次排列,但是不是依次挨着排列,要排列的数据要看自己数据成员自身长度的数和x哪个更小,假设自身数据长度为4,x为8,则再看自己要依次排列的地址是不是4的倍数,如果不是,则自动延长地址到4的倍数处,再赋值。

2:数据结构对齐:

当上面那个对齐了每一个数据成员后, 现在需要对数据结构整体长度对齐。找出数据结构成员中最大长度的数据的值z,跟x做对比,找到最小的那个,假设x为8,z为4,则整体数据结构长度应该是4的整数倍,不够自动补齐。

3:结构体作为成员

如果成员内有结构体,需要看结构体中最大的成员的长度h来做比较,是h的倍数。