c/c++ 的記憶體位元組對齊
阿新 • • 發佈:2022-05-06
記憶體對齊
先了解下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/