一,記憶體位址對齊的概念
電腦記憶體中排列、訪問資料的一種方式,包含基本資料對齊和結構體資料對齊。
32位系統中,資料匯流排寬度為32,每次能夠讀取4位元組資料。地址匯流排為32,最大定址空間為4GB。但是由於最低位A[0]~A[1]是不用於定址的,因此只能訪問4的倍數的地址空間,但是定址空間還是2^30*字長=4GB。
因此記憶體中除了結構體中成員變數之外的基本類型的開始的手地址最低兩位都是0。基本類型資料對齊就是資料在記憶體中的位移地址必須是一個字的倍數,以提高讀取資料時的效能。為了對齊資料,必須在上個資料結束和下個資料開始處插入一些位元組,這就是結構體資料對齊。
二,不進行對齊的影響
例如int a的地址是0x00fffff3,則其位元組分布在0x00fffff3~0x00fffff6空間內,為了讀取這個int,cpu必須對 0x00fffff0和0x00fffff4進行兩次記憶體讀取,並處理得出的中間結果。兩次記憶體訪問將會浪費大量的時間,因為記憶體訪問的速度遠小於CPU 處理指示的速度。
三,結構體的記憶體位址對齊
結構體本身必須是4位元組對齊的,而其成員變數則處理規則如下。
以下是Microsoft和GNU對x86架構32位系統的結構體成員的預設對齊:
char 1位元組對齊
short 2位元組對齊
int 4位元組對齊
float 4位元組對齊
double windows是8位元組對齊,linux是4位元組對齊
當某一個成員後邊的成員變數要求的地址對齊較大,則應該填入一些位元組。且總的結構體大小為最大對齊的倍數,因此最後可能還要填充一些字元。
因為上述結構體對齊的原因,將結構體成員按照大小遞增/遞減方式排序,可以減少結構體佔用的空間大小。而這樣同時使得對整個結構體的存取的效率變高了(佔用小,整個的訪問次數可以降低)。
另外一個提高效率的方法是把一些佔用位元組數較少的成員合并到位元組數佔用大的成員,形成union類型。
四。準則
其實位元組對齊的細節和具體編譯器實現相關,但一般而言,滿足三個準則:
1. 結構體變數的首地址能夠被其最寬基本類型成員的大小所整除;
2. 結構體每個成員相對於結構體首地址的位移量都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組;
3. 結構體的總大小為結構體最寬基本類型成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組。
五.基本概念
位元組對齊:電腦儲存系統中以Byte為單位儲存資料,不同資料類型所佔的空間不同,如:整型(int)資料佔4個位元組,字元型(char)資料佔一個位元組,短整型(short)資料佔兩個位元組,等等。電腦為了快速的讀寫資料,預設情況下將資料存放在某個地址的起始位置,如:整型資料(int)預設儲存在地址能被4整除的起始位置,字元型資料(char)可以存放在任何地址位置(被1整除),短整型(short)資料存放區在地址能被2整除的起始位置。這就是預設位元組對齊。
六、結構體長度求法
1.成員都相同時(或含數組且數組資料類型同結構體其他成員資料類型):
結構體長度=成員資料類型長度×成員個數(各成員長度之和);
結構體中數組長度=數組資料類型長度×數組元素個數;
2.成員不同且不含其它結構體時;
(1).分析各個成員長度;
(2).找出最大長度的成員長度M(結構體的長度一定是該成員的整數倍);
(3).並按最大成員長度出現的位置將結構體分為若干部分;
(4).各個部分長度一次相加,求出大於該和的最小M的整數倍即為該部分長度
(5).將各個部分長度相加之和即為結構體長度
3.含有其他結構體時:
(1).分析各個成員長度;
(2).對是結構體的成員,其長度按b來分析,且不會隨著位置的變化而變化;
(3).分析各個成員的長度(成員為結構體的分析其成員長度),求出最大值;
(4).若長度最大成員在為結構體的成員中,則按結構體成員為分界點分界;
其他成員中有最大長度的成員,則該成員為分界點;
求出各段長度,求出大於該和的最小M的整數倍即為該部分長度
(5).將各個部分長度相加之和即為結構體長度
七、空結構體 struct S5 { }; sizeof( S5 ); // 結果為1
“空結構體”(不含資料成員)的大小不為0,而是1。試想一個“不佔空間”的變數如何被取地址、兩個不同的“空結構體”變數又如何得以區分呢於是,“空結構體”變數也得被儲存,這樣編譯器也就只能為其分配一個位元組的空間用於佔位了。
八、有static的結構體 struct S4{ char a; long b; static long c; //靜態 };
靜態變數存放在全域資料區內,而sizeof計算棧中分配的空間的大小,故不計算在內,S4的大小為4+4=8。
#pragma pack(n)指令設定1.2.4對齊。linux下最高位4.#pragma pack()指令預設為四。
九.union
union的長度取決於其中的長度最大的那個成員變數的長度。即union中成員變數是重疊擺放的,其開始地址相同。
其實union(共用體)的各個成員是以同一個地址開始存放的,每一個時刻只可以儲存一個成員,這樣就要求它在分配記憶體單元時候要滿足兩點:
1.一般而言,共用體類型實際佔用儲存空間為其最長的成員所佔的儲存空間;
2.若是該最長的儲存空間對其他成員的元類型(如果是數組,取其類型的資料長度,例int a[5]為4)不滿足整除關係,該最大空間自動延伸;
我們來看看這段代碼:
union mm{ char a;//元長度1 int b[5];//元長度4 double c;//元長度8 int d[3]; };
本來mm的空間應該是sizeof(int)*5=20;但是如果只是20個單元的話,那可以存幾個double型(8位)呢?兩個半?當然不可以,所以mm的空間延伸為既要大於20,又要滿足其他成員所需空間的整數倍,即24
所以union的儲存空間先看它的成員中哪個占的空間最大,拿他與其他成員的元長度比較,如果可以整除就行。