一、位元組對齊的意義:
通過合理的記憶體對齊可以提高訪問效率,有效地節省儲存空間,為使cpu能夠對資料進行快速訪問,資料的起始位址應具有「對齊」特性。比如4位元組資料的起始位址應位於4位元組邊界上,即起始位址能夠被4整除。
但是在32位cpu中使用1位元組或者2位元組對齊,會降低變數訪問速度。
vc/c++和gnugcc中都是預設是4位元組對齊。
二、位元組對齊的原則
基本概念:
資料型別自身的對齊值:char型資料自身對齊值為1位元組,short型資料為2位元組,int/float型為4位元組,double型為8位元組。
結構體或類的自身對齊值:其成員中自身對齊值最大的那個值。
指定對齊值:#pragma pack (value)時的指定對齊值value。
資料成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中較小者,即有效對齊值=min。
基於上面這些值,就可以方便地討論具體資料結構的成員和其自身的對齊方式。
其中,有效對齊值n是最終用來決定資料存放位址方式的值。有效對齊n表示「對齊在n上」,即該資料的「存放起始位址%n=0」。而資料結構中的資料變數都是按定義的先後順序存放。第乙個資料變數的起始位址就是資料結構的起始位址。結構體的成員變數要對齊存放,結構體本身也要根據自身的有效對齊值圓整(即結構體成員變數占用總長度為結構體有效對齊值的整數倍)。
例1:
struct a
;struct b
;
結果:
已知32位機器上各資料型別的長度為:char為1位元組、short為2位元組、int為4位元組、long為4位元組、float為4位元組、double為8位元組。那麼上面兩個結構體大小如何呢?
sizeof(strcut a)值為8;sizeof(struct b)的值卻是12。
結構體a中包含乙個4位元組的int資料,乙個1位元組char資料和乙個2位元組short資料;
結構體b也一樣。按理說a和b大小應該都是7位元組。之所以出現上述結果,就是因為編譯器要對資料成員在空間上進行對齊。
分析:
假設b從位址空間0x0000開始存放,且指定對齊值預設為4(4位元組對齊)。成員變數b的自身對齊值是1,比預設指定對齊值4小,所以其有效對齊值為1,其存放位址0x0000符合0x0000%1=0。成員變數a自身對齊值為4,所以有效對齊值也為4,只能存放在起始位址為0x0004 ~ 0x0007四個連續的位元組空間中,符合0x0004%4=0且緊靠第乙個變數。變數c自身對齊值為 2,所以有效對齊值也是2,可存放在0x0008 ~ 0x0009兩個位元組空間中,符合0x0008%2=0。所以從0x0000 ~ 0x0009存放的都是b內容。
再看資料結構b的自身對齊值為其變數中最大對齊值(這裡是b)所以就是4,所以結構體的有效對齊值也是4。根據結構體圓整的要求, 0x0000 ~ 0x0009=10位元組,(10+2)%4=0。所以0x0000a ~ 0x000b也為結構體b所占用。故b從0x0000到0x000b 共有12個位元組,sizeof(struct b)=12。
之所以編譯器在後面補充2個位元組,是為了實現結構陣列的訪問效率。試想如果定義乙個結構b的陣列,那麼第乙個結構起始位址是0沒有問題,但是第二個結構呢?按照陣列的定義,陣列中所有元素都緊挨著。如果我們不把結構體大小補充為4的整數倍,那麼下乙個結構的起始位址將是0x0000a,這顯然不能滿足結構的位址對齊。因此要把結構體補充成有效對齊大小的整數倍。其實對於char/short/int/float/double等已有型別的自身對齊值也是基於陣列考慮的,只是因為這些型別的長度已知,所以他們的自身對齊值也就已知。
2. 基本原則
結構體變數的首位址能夠被其最寬基本型別成員的大小所整除;
結構體每個成員相對結構體首位址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組(internal adding);
結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要編譯器會在最末乙個成員之後加上填充位元組。
對於以上規則的說明如下:
第一條:編譯器在給結構體開闢空間時,首先找到結構體中最寬的基本資料型別,然後尋找記憶體位址能被該基本資料型別所整除的位置,作為結構體的首位址。將這個最寬的基本資料型別的大小作為上面介紹的對齊模數。例2:第二條:為結構體的乙個成員開闢空間之前,編譯器首先檢查預開闢空間的首位址相對於結構體首位址的偏移是否是本成員大小的整數倍,若是,則存放本成員,反之,則在本成員和上乙個成員之間填充一定的位元組,以達到整數倍的要求,也就是將預開闢空間的首位址後移幾個位元組。
第三條:結構體總大小是包括填充位元組,最後乙個成員滿足上面兩條以外,還必須滿足第三條,否則就必須在最後填充幾個位元組以達到本條要求。
/* offset巨集定義可取得指定結構體某成員在結構體內部的偏移 */
#define offset(st, field) (size_t)&(((st*)0)->field)
typedef structt_test;
int main(void)
輸出:
1 size = 16
2 a-0, b-2, c-4, d-8
3 e[0]-12, e[1]-13, e[2]-14
分析:
char a占用1個位元組,沒問題。
short b本身占用2個位元組,根據上面準則2,需要在b和a之間填充1個位元組。
char c占用1個位元組,沒問題。
int d本身占用4個位元組,根據準則2,需要在d和c之間填充3個位元組。
char e[3];本身占用3個位元組,根據原則3,需要在其後補充1個位元組。
因此,sizeof(t_test) = 1 + 1 + 2 + 1 + 3 + 4 + 3 + 1 = 16位元組。
參考:
參考位址對齊:
c語言位元組對齊
現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體位址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這就是對齊。基本資料型別自身對齊,也叫自然對齊。就是說...
C語言位元組對齊
一 概念 對齊跟資料在記憶體中的位置有關。如果乙個變數的記憶體位址正好位於它長度的整數倍,他就被稱做自然對齊。比如在32位cpu下,假設乙個整型變數的位址為0x00000004,那它就是自然對齊的。二 為什麼要位元組對齊 需要位元組對齊的根本原因在於cpu訪問資料的效率問題。假設上面整型變數的位址不...
C語言位元組對齊
一 概念 對齊跟資料在記憶體中的位置有關。如果乙個變數的記憶體位址正好位於它長度的整數倍,他就被稱做自然對齊。比如在32位cpu下,假設乙個整型變數的位址為0x00000004,那它就是自然對齊的。二 為什麼要位元組對齊 在c語言中,結構是一種復合資料型別,其構成元素既可以是基本資料型別 如int ...