爲何會出現內存對齊?編程
由於當CPU訪問內存對齊的數據時,它的運行效率是很是高的。當CPU試圖讀取的數值沒有正確的對齊時,CPU能夠執行兩種操做之一:產生一個異常條件;執行屢次對齊的內存訪問,以便讀取完整的未對齊數據,若屢次執行內存訪問,應用程序的運行速度就會慢。因此計算機採用內存對齊的方式來存儲數據。數組
這是高效編程一種很重要的思想:以空間換時間ide
關於結構體內存對齊(沒有指定#pragma pack宏的狀況):
spa
規則1:數據成員對齊規則:結構體的數據成員,第一個數據成員放在offset(偏移量)爲0的地方,之後每一個數據成員存儲的起始位置要從該成員大小的整數倍開始(好比int在32位機爲4字節,則要從4的整數倍地址開始存儲)。3d
規則2:結構體做爲成員:若是一個結構裏有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲。(struct a裏存有struct b,b裏有char,int,double等元素,那b應該從8的整數倍開始存儲。)對象
規則3:收尾工做:結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。blog
看下面這個例子:內存
void Test1() { struct A { int a; //0-3 double d; //4-11 char c; //12 }; cout<<"sizeof(A)= "<<sizeof(A)<<endl; }
先看規則1:第一個成員放在offset爲0的位置,a(0-3),第二個數據d起始位置爲第一個成員大小的整數倍開始,因此從4開始,double佔8個字節,因此d存放位置offset爲:(4-11),接下來的成員c,第一個成員整數倍爲它的起始位置,因此起始位置offset爲:(12),因此c存放位置(12),這就將數據按內存對齊方式存好了,可是結構體A的大小是多少呢?還要使用規則3:是結構體內部所佔字節最大的成員的最小整數倍。,最大成員是d(佔8個字節),因此sizeof(A) = 24,結果顯示:get
再看下面這個例子:編譯器
void Test2() { struct B { int a; //0-3 char c; //4 double d; //8-15 }; cout<<"sizeof(B) = "<<sizeof(B)<<endl; }
仍是先看規則1:第一個成員放在offset爲0的位置,a(0-3),第二個數據d起始位置爲第一個成員大小的整數倍開始,因此從4開始,char佔一個字節,因此成員c存放位置offset爲:(4),第三個數據成員起始位置依舊爲第一個成員大小的整數倍,起始位置offset爲(8),double所佔字節爲(8),因此d存放位置offset(8-15),再看規則3:結構體總大小,是結構體內部所佔字節最大的成員的最小整數倍。最大成員爲d,d佔8個字節,因此sizeof(B) = 16,顯示結果:
看這個例子:
void Test3() { struct C {}; cout<<"sizeof(C) = "<<sizeof(C)<<endl; }//[cpp] class D {}; void Test4() { cout<<"sizeof(D) = "<<sizeof(D)<<endl; }
這個大小又是多少呢?哈哈..
由於在C++標準中規定:任何不一樣的對象不能擁有相同的內存地址。若是空類大小爲0,若咱們聲明一個這個類的對象數組,那麼數組中的每一個對象都擁有了相同的地址,這顯然是違背標準的。
這時編譯器就會加一個字節來區別,因此sizeof(C) = 1,sizeof(D) = 1,顯示結果:
總結一下:當結構體中沒有嵌套時,只須要使用規則1和規則3就能夠計算出結構體大小。
再來看下面這個例子:
void Test5() { struct A { int a; //0-3 char c; //4 double d; //8-15 };//0-15 struct B { A a; //0-15 int b; //16-19 double c; //20-27 };//0-31 cout<<"sizeof(B) = "<<sizeof(B)<<endl; }
仍是先來看規則1來計算出結構體A的大小爲16,在結構體B中,a的大小爲16,結構體嵌套時就要運用規則2:結構體a的起始位置offset爲結構體A中最大成員的整數倍,因此在結構體B中成員a的起始位置offset爲:(0),所佔大小爲16,因此存放位置offset爲(0-15),而B的數據成員依舊按照規則1進行存儲:因此b的起始位置offset爲(16),存放位置offset爲(16-19),B中成員c的起始位置offset爲(20),存放位置offset爲(20-27),再依據規則3,結構B的總大小爲,結構體B中結構體內部所佔字節最大的成員的最小整數倍,不足的要補齊offset(28-31),因此sizeof(B) = 32,結構顯示:
總結一下:當出現結構體嵌套時,首先依據規則1,計算出嵌套的結構體大小,再根據規則2,肯定嵌套的結構體存儲的起始位置offset,再依據規則1,肯定其其餘成員的存儲位置,最後依據規則3,肯定結構體其大小。
當指定了#pragma back宏時
看下面這個例子:
#pragma pack(4) //指定默認最大對齊數爲4 void Test6() { struct A { char c1; //0 int i; //4-7 double d; //8-15 char c2; //16 }; cout<<"sizeof(A) = "<<sizeof(A)<<endl; }
當沒有指定#pragma back宏時,依據規則1和規則3,sizeof(A) = 24,有了默認最大對齊數,規則3就不在適用了,應該改成指定最大默認對齊數的最小整數倍,因此sizeof(A) = 20。顯示結果:
那麼再來看下面這個例子:
#pragma pack(4) void Test7() { struct A { char arr[7]; short s; }; cout<<"sizeof(A) = "<<sizeof(A)<<endl;; }
因爲有了上面的Test6(),就能夠知道:sizeof(A) = 12,顯示結果:
由於結構體A中最大對齊數比默認對齊數小,因此使用規則3時,就使用結構體中的最大對齊數。
總結一下:當有指定默認對齊數時,就要分狀況考慮了。
若是默認對齊數比結構體內部的最大對齊數小,那麼使用規則3時,結構體大小就是默認對齊數的最小整數倍;
若是默認對齊數比結構體內部的最大對齊數大,就直接使用規則3.