結構體位元組對齊筆記(Mac OS 10.9 中測試)
前言
結構體位元組對齊相關的文章網路上有很多,看了其中幾篇寫的不錯的之後,做了一點總結,僅供自己日後回憶,請網友忽略。看過眾多關於位元組對齊的博文中,寫的我認為比較好的是實驗室蘇同學(ACM大神)寫的http://c4fun.cn/blog/2014/01/11/struct-alignment/,本文有部分內容摘自這篇部落格。另外Wiki上面的位元組對齊講的非常好,雖然是英文的。位元組對齊的目的很簡單,就是提高CPU讀取的效率。例如,32位機,CPU一次可以讀32位,那麼一個4位元組的資料的起始地址剛好在4的倍數上,那麼CPU一次就可以讀完,否則,CPU要讀取兩次。
記憶體對齊方法:
1、確定位元組對齊值
1)基本資料自身對齊值:基本資料型別的自身對齊值,一般由編譯器及系統決定;
2)結構體自身對齊值:取決於結構體成員中自身對齊值的最大值;
3)指定對齊值:通過#pragma pack(n)巨集指定的對齊值;
4)有效對齊值:取決於自身對齊值和指定對齊值中的較小值。
Wiki上32位系統的位元組對齊值。
- A char (one byte) will be 1-byte aligned.
- A short (two bytes) will be 2-byte aligned.
- An int (four bytes) will be 4-byte aligned.
- A long (four bytes) will be 4-byte aligned.
- A float (four bytes) will be 4-byte aligned.
- A double (eight bytes) will be 8-byte aligned on Windows and 4-byte aligned on Linux (8-byte with -malign-double compile time option).
- A long long (eight bytes) will be 8-byte aligned.
- A long double (ten bytes with C++Builder and DMC, eight bytes with Visual C++, twelve bytes with GCC) will be 8-byte aligned with C++Builder, 2-byte aligned with DMC, 8-byte aligned with Visual C++ and 4-byte aligned with GCC.
- Any pointer (four bytes) will be 4-byte aligned. (e.g.: char*, int*)
The only notable difference in alignment for a 64-bit system when compared to a 32-bit system is:
- A long (eight bytes) will be 8-byte aligned.
- A double (eight bytes) will be 8-byte aligned.
- A long double (eight bytes with Visual C++, sixteen bytes with GCC) will be 8-byte aligned with Visual C++ and 16-byte aligned with GCC.
- Any pointer (eight bytes) will be 8-byte aligned.
值得注意的是:
如果用#pragma pack(n)指定對齊值的話,真正的位元組對齊值會取min(n,型別本身的對齊值)。
2.根據對齊值進行對齊
對齊方法遵循下面兩條規則,源自C4FUN博文。(有的博文上是4條規則,化簡了就是這兩條)
1)假設結構體的起始位置為0,結束位置為n,n+1必須是結構體有效對齊值的倍數。(如int型別,假設起始位置為0,結束位置為3,則n+1=4一定為有效對齊值的倍數)
2)假設某成員的起始對齊值是m,m必須是該成員有效對齊值的倍數。
實驗結果
我在Mac Os 10.9(64bit)系統下進行測試,clang+Xcode5編譯。程式碼如下:#include <iostream>
typedef struct _C{
int _int_32;
char _char_8;
int* _ptr_64;
short _short_16;
}C;
typedef struct __C{
int _int_32;
char _char_8;
short _short_16;
int* _ptr_64;
}C_;
int main(int argc, const char * argv[])
{
std::cout <<"sizeof int ="<<sizeof(int)<<std::endl;
std::cout <<"sizeof char ="<<sizeof(char)<<std::endl;
std::cout <<"sizeof int*(pointer) ="<<sizeof(int*)<<std::endl;
std::cout <<"sizeof short ="<<sizeof(short)<<std::endl;
std::cout <<"sizeof _C ="<<sizeof(C)<<std::endl;
std::cout <<"sizeof __C ="<<sizeof(C_)<<std::endl;
return 0;
}
命令列輸出:
sizeof int =4
sizeof char =1
sizeof int*(pointer) =8
sizeof short =2
sizeof _C =24
sizeof __C =16
從輸出結果可以看出,第一個結構體的size為24位元組,第二個位16位元組。
分析結構體_C的記憶體佈局:
而結構體__C的記憶體佈局:
結構體_C中為了為了滿足規則1,在地址5-7位置處填補了3個padding位元組。為了滿足規則2,在地址18-24處,填補了6個位元組的padding。而結構體__C中,將short的編排順序改變之後,剛好滿足了規則2。所以節省了8個位元組。可見,有效的利用變數編排順序,可以達到優化程式效能、縮小程式記憶體佔用的目的,以後寫結構體的時候不能隨意編排順序,應按照位元組對齊的規則,以最節省空間的方式編排。