why對齊?
為了提高尋址效率。
計算機內存是以字節(Byte)為單位劃分的,理論上CPU可以訪問任意編號的字節,但實際情況并非如此。
以32位的CPU為例,實際尋址的步長為4個字節,也就是只對編號為 4 的倍數的內存尋址,例如 0、4、8、12、1000 等,而不會對編號為 1、3、11、1001 的內存尋址。
對于程序來說,一個變量最好位于一個尋址步長的范圍內,這樣一次就可以讀取到變量的值;如果跨步長存儲,就需要讀取兩次,然后再拼接數據,效率顯然降低了。例如一個 int 類型的數據,如果地址為 8,那么很好辦,對編號為 8 的內存尋址一次就可以。如果編號為 10,就比較麻煩,CPU需要先對編號為 8 的內存尋址,讀取4個字節,得到該數據的前半部分,然后再對編號為 12 的內存尋址,讀取4個字節,得到該數據的后半部分,再將這兩部分拼接起來,才能取得數據的值。
將一個數據盡量放在一個步長之內,避免跨步長存儲,這就是所謂的內存對齊了。
內存對齊規則
以結構體為例。以下來自百度百科。
1)結構體變量的首地址是其最長基本類型成員的整數倍;
2)結構體每個成員相對于結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充字節(internal adding);
備注:為結構體的一個成員開辟空間之前,編譯器首先檢查預開辟空間的首地址相對于結構體首地址的偏移是否是本成員的整數倍,若是,則存放本成員,反之,則在本成員和上一個成員之間填充一定的字節,以達到整數倍的要求,也就是將預開辟空間的首地址后移幾個字節。
3)結構體的總大小為結構體最寬基本類型成員大小的整數倍,如有需要,編譯器會在最末一個成員之后加上填充字節(trailing padding)。
備注:
a、結構體總大小是包括填充字節,最后一個成員滿足上面兩條以外,還必須滿足第三條,否則就必須在最后填充幾個字節以達到本條要求。
b、如果結構體內存在長度大于處理器位數的元素,那么就以處理器的倍數為對齊單位;否則,如果結構體內的元素的長度都小于處理器的倍數的時候,便以結構體里面最長的數據元素為對齊單位。
- 結構體內類型相同的連續元素將在連續的空間內,和數組一樣。
舉例驗證
# include <iostream>
using namespace std;
struct MyStruct
{
char b; //1
int a; //4
char d; //1
};
int main()
{
cout << sizeof MyStruct << endl; // 12
system("pause");
return 0;
}
對于以上程序結果為12。(1—4—1 ——>>12)
而稍微改變順序(1—1—4 ——>>8)。
大家可以根據以上規則自己體會。
總結
(1)最后的長度一定是結構體中最長元素所占字節的整數倍。
(2)前面的地址必須是后面的地址正數倍,不是就補齊。