結構體內存對齊

爲何會出現內存對齊?編程

    由於當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

wKiom1ca9E3i6CQIAAAK0zYHweA851.png

再看下面這個例子:編譯器

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,顯示結果:

wKioL1ca-D_QTJVFAAALhOSm82k810.png

看這個例子:

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,顯示結果:

wKiom1ca_eSBDeDKAAALcWen7E0306.png

總結一下:當結構體中沒有嵌套時,只須要使用規則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,結構顯示:

wKioL1cbCcaA8DkxAAALj2BbmKI626.png

總結一下:當出現結構體嵌套時,首先依據規則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。顯示結果:

wKiom1cbDhbD_lOmAAALes8dTTM154.png

那麼再來看下面這個例子:

#pragma pack(4)
void Test7()
{
	struct A
	{
		char arr[7];
		short s;
	};
	cout<<"sizeof(A) = "<<sizeof(A)<<endl;;
}

因爲有了上面的Test6(),就能夠知道:sizeof(A) = 12,顯示結果:

wKiom1cbD3_C_IxVAAAMYeyRjo8847.png

爲何是10,瞬間懵逼...j_0012.gif

由於結構體A中最大對齊數比默認對齊數小,因此使用規則3時,就使用結構體中的最大對齊數。

總結一下:當有指定默認對齊數時,就要分狀況考慮了。

    若是默認對齊數比結構體內部的最大對齊數小,那麼使用規則3時,結構體大小就是默認對齊數的最小整數倍;

    若是默認對齊數比結構體內部的最大對齊數大,就直接使用規則3.

相關文章
相關標籤/搜索