許多計算機系統對基本數據類型的合法地址做出了一些限制,要求某種類型對象的地址必須是某個值K(通常是2,、4、8)的倍數,這種對齊限制簡化了形成處理器和存儲器系統之間接口的硬件設計,並且提高了存取效率,雖然可能浪費了空間。所以瞭解對齊,可以使我們在不降低效率的情況下以最低的空間存儲數據
不同的硬件結構對對齊有不同的要求,IA32(Intel Architecture 32)硬件無論數據是都對齊,都能正常工作,但是,Intel還是建議要數據對齊以提高存儲器系統性能。
對Linux系統來說,2字節數據類型(如short)的地址必須是2的倍數,而較大的數據類型(int、int*、float、double)的地址必須是4的倍數。
對windows不一樣,double或者long long類型數據的地址是8的倍數。
看一下測試代碼:(win7 32bit VC++平臺)
#include <stdio.h>
struct A {
char c;
int i;
short s;
};
struct B {
char c;
short s;
int i;
};
#pragma pack(1) //按1字節對齊
struct C {
char c;
short s;
int i;
};
#pragma pack() //取消對齊
struct Test {
char c;
int i ;
float f;
double d;
int *pa;
char *pc;
short s;
};
struct TestOptimize {
char c;
short s;
int i ;
float f;
double d;
int *pa;
char *pc;
};
#pragma pack(1)
struct Test1 {
char c;
int i ;
float f;
double d;
int *pa;
char *pc;
short s;
};
#pragma pack()
int main(){
printf("%d\n",sizeof(A));
printf("%d\n",sizeof(B));
printf("%d\n",sizeof(C));
printf("%d\n",sizeof(Test));
printf("%d\n",sizeof(TestOptimize));
printf("%d\n",sizeof(Test1));
return 0;
}
打印結果:
12
8
7
40
32
27
對於A,char後面補齊是爲了int能夠對齊,但是爲什麼short後面也插入兩個字節呢,因爲A可能被申明爲一個數組,如果是數組,也必須滿足每個元素地址是4的倍數對齊,因此會被補齊。結構體默認會以佔用空間最大的那個字段的字節數倍數爲地址值
並且可以看出B是對A的一種優化,調整一下字段存儲順序,就可以節省出一些空間,並且不影響存取效率
同樣的代碼,我們在linux系統下再做測試(Ubuntu 14.04.2 LTS \n \l x86_64 x86_64 x86_64 GNU/Linux (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ))我的是ubuntu64位系統
輸出結果:
12
8
7
48
40
35
這裏Test 和TestOptimize和Test1都不一樣了。因爲64位系統指針類型是64bit即8 B。下面是在win7 32 VC6.0下Test/TestOptimize/Test1與ubuntu64 gcc4.8.2下面的對比。
總結:
1.字節對齊是和硬件相關的限制,一些硬件必須要字節對齊(從而提高訪問效率),另外一些硬件可以支持對齊或者不對齊,像IA32架構,但是建議字節對齊,這樣能夠提高存儲器效率。
2.基本數據類型存放首地址一般是該數據類型自身長度的整數倍,一般都是1、2、4、8的倍數。對於複合數據類型,像結構體,首地址需要是他成員中最長字段的整數倍。
3.另外,結構體爲了滿足結構體數組的存儲要求,可能在最後面也補0以便下一個元素的首地址也是對齊的。計算結構體所佔的內存空間有一個小技巧,就是畫位圖。通過最長字段確定位圖的寬度,然後再根據各個字段首地址對齊要求,畫出整個位圖。就像我上面的圖一樣。
4.注意,不同平臺不同編譯器最後的對齊效果也不一定相同,字段的位置具體在哪個地方也是有多種可能的。