完全搞清計算結構體大小和數據對齊原則
By Qianghaohao
數據對齊:
許多計算機系統對基本數據類型合法地址作出了一些限制,要求某種類型對象的地址必須是
某個值K(一般是2,4或8)的倍數。這種對齊限制簡化了造成處理器和存儲器系統之間的接口的硬件
設計。例如,假設一個處理器老是從存儲器中取出8個字節,則地址必須爲8的倍數。若是咱們能保
證將全部的double類型數據的地址對齊成8的倍數,那麼就能夠用一個存儲器操做來讀或者寫值了。
不然,咱們可能須要執行兩次存儲器訪問,由於對象可能被分放在兩個8字節存儲塊中。
當數據類型爲結構體時,編譯器可能須要在結構體字段的分配中插入間隙,以保證每一個結構元素都
知足它的對齊要求。而結構自己對它的地址也有一些對齊要求,此時可能須要在結構末尾填充一些
空間,以知足結構體總體的對齊----向結構體元素中最大的元素對齊。稍後會用代碼說明!!!
Linux和Microsoft Windows的對齊方式有何不一樣:
一.Linux的對齊策略:
在Linux中2字節數據類型(例如short)的地址必須是2的倍數,而較大的數據類型(例如int,int *
,float和double)的地址必須是4的倍數。也就是說Linux下要麼2字節對齊,要麼4字節對齊,沒
有其餘格式的對齊。
二.Microsoft Windows的對齊策略:
在Windows中對齊要求更嚴--任何K字節基本對象的地址都必須是K的倍數,K=2,4,或者8.
特別地,double或者long long類型數據的地址應該是8的倍數。能夠看出Windows的對齊策略和
Linux仍是不一樣的。稍後用代碼說明!!!
接下來用代碼和圖文說明二者的對齊方式(不一樣的對齊方式產生的結構體大小不一樣):
測試代碼以下:
/////////////////////////////////////
// filename:DataAlignment
/////////////////////////////////////
#include<stdio.h>
typedef struct
{
char c;
int i[2];
double v;
}S;
int main()
{
printf("size of S = %d\n", sizeof(S));
return 0;
}
程序中定義了一個結構體,在沒有任何數據對齊時內存佈局以下:
一.在紅帽Linux i686上編譯編譯後結構S的佈局以下:
因爲要保證結構體每一個元素都要數據對齊,所以必須在c和i[0]之間插入3字節的間隙(圖中陰影部分爲編譯器插入的間隙)
使得i[0]和後面的元素的的偏移量都爲4的倍數,這樣最終S結構大小爲20字節。
運行程序結果爲:
size of S = 20
二.在Microsoft Windows 上編譯後S的內存佈局以下:
在windows下int類型4個字節,所以int類型要向4字節對齊,double類型8字節,所以要向8字節對齊。所以在
c和i[0]之間插入3字節的間隙(圖中陰影部分),使得i[0]的偏移量爲4的倍數,同時在i[1]和v之間插入4字節的間隙,
使得v的偏移量爲8的倍數。這樣最終S結構的大小爲24字節。
運行程序結果爲:
size of S = 24
從以上對比能夠看出Linux下和windows下的對齊策略是不一樣的,這就致使在兩個平臺下結構體的大小不一樣。
如今考慮以下代碼:
#include<stdio.h>
typedef struct
{
int i;
int j;
char c;
}S1;
int main()
{
printf("size of S1 = %d\n", sizeof(S1));
return 0;
}
可能不少人認爲編譯後運行結果爲9,覺得結構體的每一個元素都知足各自的對齊要求。其實否則,別忘了還有要考慮
結構體總體的對齊。假若有以下聲明:
S1 d[4];
若是這樣分配9個字節,不可能知足d的每一個元素的對齊要求,由於這些元素的地址分別爲xd,xd+9,xd+18,
xd+27。這樣只有第一個元素知足4字節對齊的要求,而其餘的元素的地址都不是4的倍數。
所以編譯器會在結構體的末尾填充3字節知足結構體總體的對齊要求,填充後的內存佈局以下:
這樣一來,d的元素的地址分別爲xd,xd+12,xd+24,xd+36,知足4字節的整數倍,這樣最終S1的大小
爲12字節,而不是9字節.
總結:經過代碼的對比,能夠看出Linx和Windwos的數據對齊有所差別,這就致使
在一些狀況下二者平臺結構體類型大小的不一樣。經過以上示例分析咱們能夠很簡單的
計算出在任何平臺下結構體的大小。讀者能夠找相關的練習題繼續練習下,驗證結果
的正確性。相信仔細閱讀本文應該能搞定全部相似的問題!!!
本文參考書籍: