struct記憶體對齊 詳解

2021-06-06 16:43:31 字數 2611 閱讀 5609

結構體的對齊

在sizeof計算乙個結構體的大小時,經常得到的值比結構體內部成員所佔記憶體總和要大,這就是因為在結構體內部,成員在儲存時有對齊的規則。結構體對齊指的是:編譯器向結構體插入無用記憶體的能力,插入無用記憶體使得結構體成員以最佳方式對齊,從而得到更高的效能。當基本資料型別以位元組位址(幾倍於自身大小)儲存時,很多處理器能夠獲得最佳效能。

以下是幾個例子:

struct x ;

struct y ;

struct z ;

sizeof(x)=12;   sizeof(y)=8;    sizeof(z)=8;

x的記憶體布局:s             i             c

11**          1111          1***

y的記憶體布局:i             c             s

1111          1*            11

z的記憶體布局:i             s             c

1111           11            1*

其中*表示填充的位元組,x中s後面為什麼要填充兩個位元組?因為i是整型,其起始位置要為4的倍數。c後面要填充3個位元組,因為結構體size要為4(即最大型別——整型sizeof(int))的倍數。

y中c後面填充乙個位元組,因為s為short型別,起始位置要為2的倍數。s後面沒有填充,因為c和s正好占用了4個位元組。

z中s後面沒有填充,因為s和c正好占用4個位元組,c填充乙個位元組因為struct大小要為int的整數倍。

再看乙個有結構體作為成員的例子:

struct a ;

struct b ;

sizeof(a)=24; 因為int為4,double為8,float為4,總長為8的倍數,補齊,所以為24。sizeof(b)=48;看一下b的布局

b的記憶體布局:e        f         g         h                     i

11**    1111     11111111   11******    1111****, 11111111,1111****

i其實就是a的記憶體布局。i的起始位置要為24的倍數,所以h後面要補齊(a的最大型別是double,佔8個位元組,所以i開始要8位元組對齊,即8的倍數,所以h要填充)。

通過把最大的資料型別放在結構體的開始,最小資料型別放在結構體的最後,這樣可以得到最小的結構體size。

通過上面的例子可以總結一下三個規律:

1 資料成員對齊,結構體(或聯合體)的資料成員,第乙個資料成員放在offset為0的地方,以後每個資料成員儲存的起始位置要從該成員大小的整數倍開始(比如int在32位機為4位元組,則要從4的整數倍位址開始儲存)。

2 結構體作為成員,如果乙個結構體裡面有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍位址開始儲存(struct b裡面有struct a,a裡面有char,int,double等元素,那麼a應該從8的整數倍開始儲存)。

3 結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。

還有一種常見情況,結構體中含有位域字段。位域成員不能單獨被取sizeof值。規定int,unsigned int和bool可以作為位域型別,但編譯器幾乎都對此作了擴充套件,允許其他型別的存在。使用位於的主要目的是壓縮儲存,其大致規則:

1 如果相鄰位域字段的型別相同,且其位寬之和小於型別的sizeof大小,則後面的字段將緊鄰前乙個字段儲存,知道不能容納為止;

2 如果相鄰欄位的型別相同,但其位寬之和大於型別的sizeof大小,則後面的字段將從新的儲存單元開始,其偏移量為其型別大小的整數倍;

3 如果相鄰的位域字段的型別不同,則各編譯器的具體實現有差異,vc6採取不壓縮方式,dev-c++採取壓縮方式;

4 如果位域字段之間穿插著非位域字段,則不進行壓縮。

5 整個結構體的總大小為最寬基本型別成員大小的整數倍。

看下面例子:

struct a; a的布局為f1          f2                       f3       

111       1111***  **(11個*)       1—1(10個1)**(6個*)位域型別為short,第乙個位元組能容納f1和f2,所以f2被壓縮到第1個位元組中,而f3只能從下乙個位元組開始。因此sizeof(a)=2。

struct b;相鄰位域的型別不同,在vc6中sizeof為6,在dev-c++中為2.

struct c;非位域字段插在其中,不會產生壓縮,大小為3。

補充一下聯合體:聯合體的成員共享一片記憶體空間,聯合體的成員可以使任何資料型別,用來儲存聯合體的位元組數至少要能夠儲存最大的 成員。多數情況下聯合體包含兩種或多種資料型別,但同一時間只能引用乙個成員。

例如聯合體:union aa; sizeof(aa)=sizeof(float)=4;  某一時刻對a賦值 aa.a=20; 以後引用過程只能用aa.a 。例如 printf("aa.a=%d\n",aa.a);則輸出aa.a=20。  如果這樣使用 printf("aa.b=%f\n",aa.b);則輸出結果是不確定的。同樣,如果某時刻對b賦值 aa.b=2.4,以後只能引用aa.b。printf("aa.b=%f\n",aa.b);則輸出2.4;如果printf("aa.a=%d\n", aa.a);則輸出不確定。

struct記憶體對齊

出於速度和空間的考量,編譯器在實現過程中均會採用對struct內的變數進行記憶體對齊的操作,雖然會有一定的空間浪費,卻可以減少在讀取資料時候的讀取操作。先看下面的例子 struct a int main struct b int main struct b int main struct a str...

struct記憶體對齊

關於c 中的struct記憶體對齊,應該也是初學者比較疑惑的乙個知識點,但是搞清楚之後會發現非常簡單,這裡解釋一下struct記憶體到底怎麼對齊。主要記住以下兩點 1.各成員變數存放的起始位址相對於結構的起始位址的偏移量必須為該變數的型別所占用的位元組數的倍數 2.整個struct的記憶體大小需為s...

struct 記憶體對齊

struct 是一種復合資料型別,其構成元素既可以是基本資料型別 如 int long float 等 的變數,也可以是 一些復合資料型別 如 array struct union 等 的資料單元。對於結構體,編譯器會自動進行成員變數的對齊,以提高運算效率。預設情況下,編譯器為結構體的每個成員按其自...