c/c++ 的内存字节对齐
内存对齐
先了解下C/C++基本类型的字节占用情况,
alignas 关键字
#pragma pack(N)
pragma pack 规定的对齐长度,实际使用的规则是:
- 结构(如struct,union,或者class )内部的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
- 而结构之间整体的对齐,则按照结构中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。
也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。
再来看个例子:
#include <iostream> using namespace std; #define OFFSET(struct_type, member) ((size_t) &((struct_type *) 0)->member) #pragma pack(4) struct TestB { int aa; // 第一个成员,放在[0,3]偏移的位置, char a; // 第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。 short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。 char c; //第四个,自身长为1,放在[8]的位置。 }; #pragma pack() int main() { cout<< sizeof(TestB) << endl; // 12 cout<< OFFSET(TestB, aa) <<endl; // 0 cout<< OFFSET(TestB, a) <<endl; // 4 cout<< OFFSET(TestB, b) <<endl; // 6 cout<< OFFSET(TestB, c) <<endl; // 8 }
上面的例子中,这个struct 实际占据的内存空间是9字节,但sizeof返回的是12,原因:
结构之间的对齐,是按照结构内部最大的成员的长度,和#pragma pack规定的值之中较小的一个对齐的。
所以这个例子中,结构之间对齐的长度是min(sizeof(int), 4),也就是4。 4的倍数且比8(最后一个成员的偏移)大的是12,所以整个结构的占用字节数是12。
上面的例子中,去掉第一个int成员再来看:
#pragma pack(4) struct TestB { // int aa; char a; // 第一个成员,放在[0]偏移的位置 short b; // 第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置 char c; //第三个,自身长为1,放在[4]的位置。 }; #pragma pack() int main() { cout<< sizeof(TestB) << endl; // 6 // cout<< OFFSET(TestB, aa) <<endl; cout<< OFFSET(TestB, a) <<endl; // 0 cout<< OFFSET(TestB, b) <<endl; // 2 cout<< OFFSET(TestB, c) <<endl; // 4 }
这个例子中,结构之间对齐的长度是min(sizeof(short), 4),也就是2。 2的倍数且比4(最后一个成员的偏移)大的是6,所以整个结构的占用字节数是6。
换一种修改,只修改pragma的值,
#pragma pack(2) struct TestB { int aa; // 第一个成员,放在[0,3] char a; // 二个成员,自身长1,放在[4] short b; // 第三个成员,自身长2,#pragma pack(2),取2,按2字节对齐,所以放在偏移[6,7]的位置 char c; //第四个,自身长为1,放在[8]的位置。 }; #pragma pack() int main() { cout<< sizeof(TestB) << endl; // 10 cout<< OFFSET(TestB, aa) <<endl; // 0 cout<< OFFSET(TestB, a) <<endl; // 4 cout<< OFFSET(TestB, b) <<endl; // 6 cout<< OFFSET(TestB, c) <<endl; // 8 }
更多例子:
有数组的情况:
#pragma pack(4) struct TestB { float a[5]; // 第一个成员,放在[0, 19] char b; // 第二个成员,自身长1,放在[20] float c[7]; // 第三个成员,自身长4,#pragma pack(4),取4,按4字节对齐,所以放在偏移[24,51]的位置 char d; //第四个,自身长为1,放在[52]的位置。 }; #pragma pack() int main() { cout<< sizeof(TestB) << endl; // 56 cout<< OFFSET(TestB, a) <<endl; // 0 cout<< OFFSET(TestB, b) <<endl; // 20 cout<< OFFSET(TestB, c) <<endl; // 24 cout<< OFFSET(TestB, d) <<endl; // 52 }
修改pack参数:
#pragma pack(2) struct TestB { float a[5]; // 第一个成员,放在[0, 19] char b; // 第二个成员,自身长1,放在[20] float c[7]; // 第三个成员,自身长4,#pragma pack(2),取2,按2字节对齐,所以放在偏移[22,49]的位置 char d; //第四个,自身长为1,放在[50]的位置。 }; #pragma pack() int main() { cout<< sizeof(TestB) << endl; // 52 cout<< OFFSET(TestB, a) <<endl; // 0 cout<< OFFSET(TestB, b) <<endl; // 20 cout<< OFFSET(TestB, c) <<endl; // 22 cout<< OFFSET(TestB, d) <<endl; // 50 }
参考:
http://www.yebangyu.org/blog/2015/12/30/falsesharing/