结构体内存对齐
参考:https://docs.microsoft.com/en-us/cpp/preprocessor/pack?view=msvc-160
https://blog.csdn.net/aidem_brown/article/details/77540527
https://zhuanlan.zhihu.com/p/93822540
https://zhuanlan.zhihu.com/p/30007037
格式:
#pragma pack( show )
#pragma pack( push
[ ,
identifier
] [ ,
n
] )
#pragma pack( pop
[ ,
{ identifier
| n
} ] )
#pragma pack(
[ n
] )
对于n的说明:
(Optional) Specifies the value, in bytes, to be used for packing. If the compiler option /Zp isn't set for the module, the default value for n
is 8. Valid values are 1, 2, 4, 8, and 16. The alignment of a member is on a boundary that's either a multiple of n
, or a multiple of the size of the member, whichever is smaller.
如果没有设置,则默认的n为8,用户可以通过pack设置值为:1、2、4、8、16,成员的对齐的值是:n、成员变量中最长的,两个之间的较小值。
内存对齐指令
- 一般来说,内存对齐过程对coding者来说是透明的,是由编译器控制完成的
- 如对内存对齐有明确要求,可用#pragma pack(n)指定,以n和结构体中最长数据成员长度中较小者为有效值
- 如未明确指定时,以结构体中最长的数据成员长度作为内存对齐的有效值
以下如没有特殊说明,均视为情况3(未明确指定)计算
内存对齐的三条规则
- 数据成员对齐规则,结构体(struct)(或联合(union))的数据成员,第一个数据成员存放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员(只要该成员有子成员,比如数组、结构体等)大小的整数倍开始(如:int 在 64bit 目标平台下占用 4Byte,则要从4的整数倍地址开始存储)
- 结构体作为成员,如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储
- 结构体的总大小,即sizeof的结果,必须是其内部最大成员长度(即前面内存对齐指令中提到的有效值)的整数倍,不足的要补齐
例如,如下的两个结构体,虽然成员相同,但是由于顺序不同,导致两个结构体的大小不同
struct myStruct { char c; int val1; double val2; }; struct myStruct2 { char c; double val2; int val1; };
由于没有使用#pragma pack 命令,则默认的为8
对于myStruct:
成员c所为第一个数据成员,存放在offset为0的地方,val1为int,需要存放在4的倍数的位置,因此在第一个成员c后补充3个字节,对于成员val2,为double,需要放在8的倍数,在前两个成员放置后,刚好在8的倍数上,因此:总的大小为:4+4+8=16,且结构体的总长度为16,为8的倍数,因此不需要再填充。
对于myStruct2:
成员c所为第一个数据成员,存放在offset为0的地方;
第二个成员为double,需要放在8的倍数上,因此在第一个成员后补充7个byte;
第三个为val1,int类型,占用4个byte,
因此:总的大小为:8+8+4=20,但是总长度为20,不是8的倍数(规则3),因此还需要再填充4个byte,最终的长度为:24
对于class,情况更加复杂,还需要考虑到类中是否有虚函数,虚表指针等;