最近闲来无事,翻阅msdn,在预编译指令中,翻阅到#pragma pack这个预处理指令,这个预处理指令为结构体内存对齐指令,偶然发现还有另外的内存对齐指令aligns(C++11),__declspec(align(#))(Microsoft专用),遂去探究二者之间的不一样点。c++
一、#pragma packless
这个指令为预处理指令,所谓与处理指令执行在程序的预处理阶段,该指令对应着编译选项/Zp,能够在vs的工程属性中设置编译选项的内存对齐,也能够利用预处理指令来设置。ide
#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n )
spa
预处理指令的用法如上,其中必选参数n为内存对齐值,有效值为一、四、八、16,默认值为8。可选参数中,show表明右警告消息显示当前的内存对齐值,push | pop 两者选其一,表明将当前的内存对齐值入、出栈。identifier跟随push | pop一同出入栈,做为表示使用。c++11
二、aligns、__declspec(align(#))code
aligns和alignof为c++标准的类型说明和运算符,其中aligns为内存对齐类型符,alignof为返回内存对齐值得运算符。对应还有Microsoft的msvc专用的__alignof()和__declspec(align(#))也能够达到一样的的效果。blog
__declspec(align(#))和aligns(#)中#为对齐值,可选的对齐值为二、四、八、1六、3二、64内存
三、区别ci
一样是对齐操做,两种不一样的对齐操做有什么区别呢。编译
字面上,一个是预处理指令,另外一个是类型说明符。
在相应的结构体内存对齐上,也存在差异。
所谓的内存对齐指的是变量分配到要求的内存地址上,这个地址必须是对齐值的整数倍,例如int型变量,默认的对齐值为数据长度,也就是4,分配到内存地址0x0001103F,该地址模上内存对齐值,也就是4。1103F%4=3,因此将int型变量分配到0x00011042上,该地址为对齐值得整数倍。
定义一个结构体,结构体内存对齐值为内存存储变量的最大默认对齐值
struct MyStruct { char member; int a; }mystruct; sizeof(mystruct) = 8,alignof(MyStruct) = 4
经过计算能够看出,结构体的对齐值为4,内存占用为8。结构体遵循内存对齐规则,结构体内部变量也遵循对齐规则,其中char偏移量0,int偏移量为4,两变量之间填充3字节数据。
sizeof(mystruct) = 5;alignof(MyStruct) = 1
能够看出,预处理指令要求的内存对齐为1设置生效了。
__declspec(align(2))
struct MyStruct
{
char member;
int a;
}mystruct;
sizeof(mystruct) = 8;alignof(MyStruct) = 4
设置内存对齐失败了。
struct alignas(2) MyStruct { char member; int a; }mystruct;
这下vs直接报错,'MyStruct': Alignment specifier is less than actual alignment (4), and will be ignored
经过这个报错,能够看出,类型说明符形式的内存对齐要求最小的对齐值为结构体的对齐默认值,也就是内存变量的最大对齐 值。
__declspec(align(8)) struct MyStruct { char member; int a; }mystruct; sizeof(mystruct) = 8;alignof(MyStruct) = 8 struct alignas(8) MyStruct { char member; int a; }mystruct;
sizeof(mystruct) = 8;alignof(MyStruct) = 8
其中char偏移为0,int 的偏移为4,中间填充3字节数据
struct MyStruct { alignas(8) char member; alignas(8) int a; }mystruct; sizeof(mystruct) = 16;alignof(MyStruct) = 8
此时能够看出,结构体对齐值为内部变量最大对齐值8,其中char变量对齐值为8,偏移量0,int对齐值为8,偏移量为8,中间 填充7字节数据,结尾填充4字节数据。可见 alignas()对于修饰的变量有效
struct alignas(16) MyStruct { char member; int a; }mystruct; sizeof(mystruct) = 16;alignof(MyStruct) = 16
sizeof(mystruct) = 16;alignof(MyStruct) = 16
其中char偏移量0,int偏移量为4,中间填充3字节数据,结尾填充8字节数据
sizeof(mystruct) = 8;alignof(MyStruct) = 4
能够看出经过预编译指令设置对齐失效了。
aligna(16)和#pragma(1) #pragma pack(1) struct alignas(8) MyStruct { char member; int a; }mystruct; sizeof(mystruct) = 8;alignof(MyStruct) = 8 设置了预编译指令和alignas(),遵循alignas()
4、总结
咱们能够经过两种方式来设置对齐,分别是#pragam pack()和alignas()/declspce(align(#))
对于#pragma pack()咱们能够设置全局对齐方式,结构体和结构体内部变量都将遵循设置的对齐参数;而alignas()对修饰的单个变量是,对齐参数有效,例:alignas()修饰结构体变量,那么结构体变量遵循该对齐参数,内部变量遵循默认的对齐参数,以上对齐方式均是对结构体变量起做用,对于数据区的其余变量无论如何设置对齐方式,还会按照默认的字节来对齐。
#pragma pack()这种对齐参数设置有最大上限,最大设置为其默认对齐参数;alignas()/declspce(align(#))这种对齐方式存在最小下限,最小下限为默认对齐参数。对于设置两种对齐方式,优先遵循alignas()这种对齐方式。结构体的默认对齐方式为4byte,最终的结构体对齐方式与结构体内所占最大空间的项的对齐方式一致结构体所占空间必定为对齐参数的整数倍,也就意味着结构内部可能会存在填充的字节