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/