位元組對齊與結構體大小

2021-06-22 00:00:49 字數 3315 閱讀 6099

先讓我們看四個重要的基本概念:

1.資料型別自身的對齊值:

對於char型資料,其自身對齊值為1,對於short型為2,對於int,float,double型別,其自身對齊值為4,單位位元組。

2.結構體或者類的自身對齊值:其成員中自身對齊值最大的那個值。

3.指定對齊值:#pragma pack (value)時的指定對齊值value。

4.資料成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中小的那個值。

有了這些值,我們就可以很方便的來討論具體資料結構的成員和其自身的對齊方式。有效對齊值n是最終用來決定資料存放位址方式的值,最重要。有效對齊n,就是表示「對齊在n上」,也就是說該資料的"存放起始位址%n=0".而資料結構中的資料變數都是按定義的先後順序來排放的。第乙個資料變數的起始位址就是資料結構的起始位址。結構體的成員變數要對齊排放,結構體本身也要根據自身的有效對齊值圓整(就是結構體成員變數占用總長度需要是對結構體有效對齊值的整數倍,結合下面例子理解)。這樣就不能理解上面的幾個例子的值了。

例子分析:

分析例子b;

struct b

;假設b從位址空間0x0000開始排放。該例子中沒有定義指定對齊值,在筆者環境下,該值預設為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。根據結構體圓整的要求,0x0009到0x0000=10位元組,(10+2)%4=0。所以0x0000a到0x000b也為結構體b所占用。故b從0x0000到0x000b共有12個位元組,sizeof(struct b)=12;其實如果就這乙個就來說它已將滿足位元組對齊了,因為它的起始位址是0,因此肯定是對齊的,之所以在後面補充2個位元組,是因為編譯器為了實現結構陣列的訪問效率,試想如果我們定義了乙個結構b的陣列,那麼第乙個結構起始位址是0沒有問題,但是第二個結構呢?按照陣列的定義,陣列中所有元素都是緊挨著的,如果我們不把結構的大小補充為4的整數倍,那麼下乙個結構的起始位址將是0x0000a,這顯然不能滿足結構的位址對齊了,因此我們要把結構補充成有效對齊大小的整數倍.其實諸如:對於char型資料,其自身對齊值為1,對於short型為2,對於int,float,double型別,其自身對齊值為4,這些已有型別的自身對齊值也是基於陣列考慮的,只是因為這些型別的長度已知了,所以他們的自身對齊值也就已知了.

同理,分析上面例子c:

#pragma pack (2) /*指定按2位元組對齊*/

struct c

;#pragma pack () /*取消指定對齊,恢復預設對齊*/

第乙個變數b的自身對齊值為1,指定對齊值為2,所以,其有效對齊值為1,假設c從0x0000開始,那麼b存放在0x0000,符合0x0000%1=0;第二個變數,自身對齊值為4,指定對齊值為2,所以有效對齊值為2,所以順序存放在0x0002、0x0003、0x0004、0x0005四個連續位元組中,符合0x0002%2=0。第三個變數c的自身對齊值為2,所以有效對齊值為2,順序存放

在0x0006、0x0007中,符合0x0006%2=0。所以從0x0000到0x00007共八字節存放的是c的變數。又c的自身對齊值為4,所以c的有效對齊值為2。又8%2=0,c只占用0x0000到0x0007的八個位元組。所以sizeof(struct c)=8.

#pragma pack (n) /*指定按n位元組對齊*/   不寫這句預設n =8

設真正的對齊長度為m位元組

如果n>結構體中資料型別(包括類型別)長度最大的資料型別長度,m = 結構體中最大資料型別長度

否則 m = n

總之,真正的對齊長度m為指定對齊長度n與結構體中最大型別長度中的較小值。

規則:1. 結構體變數的首位址能夠被m所整除;

2. 結構體每個成員相對於結構體首位址的偏移量都是資料成員有效對齊值(資料成員的自身對齊值和指定對齊值的較小者)的整數倍,如有需要編譯器會在成員之間加上填充位元組;

3. 結構體的總大小為m的整數倍,如有需要編譯器會在最末乙個成員之後加上填充位元組。

舉例:(摘自:

很顯然預設對齊方式會浪費很多空間,例如如下結構: 

struct student

本來只用了11bytes(5+4+2)的空間,但是由於int型預設4位元組對齊,存放在位址能被4整除的起始位置,即:如果name[5]從0開始存放,它佔5bytes,而num則從第8(偏移量)個位元組開始存放。所以sizeof(student)=16。於是中間空出幾個位元組閒置著。但這樣便於計算機快速讀寫資料,是一種以空間換取時間的方式。其資料對齊如下圖:

|char|char|char|char|
|char|----|----|----|
|--------int--------|
|--short--|----|----|
如果我們將結構體中變數的順序改變為: 

struct student

則,num從0開始存放,而name從第4(偏移量)個位元組開始存放,連續5個位元組,score從第10(偏移量)開始存放,故sizeof(student)=12。其資料對齊如下圖:

|--------int--------|
|char|char|char|char|
|char|----|--short--|
如果我們將結構體中變數的順序再次改為為: 

struct student

則,sizeof(student)=12。其資料對齊如下圖:

|--------int--------|
|--short--|char|char|
|char|char|char|----|

結構體與位元組對齊

在結構體的定義中,經常遇到位元組對齊的問題,尤其是在網路程式中。一 什麼是位元組對齊?在計算乙個結構體的sizeof值時,並不是簡單了計算結構體各成員的大小的和,而應考慮到各成員間和整個結構體的位元組對齊問題。如struct s sizeof struct s 的結果並不是1 4 2 7,而是1 3...

計算結構體大小(位元組對齊)

一,預設對齊方式 位元組對齊的細節和具體編譯器實現相關,但一般而言,滿足以下三個準則 也是vc預設對齊方式 1,結構體變數的首位址能夠被其最寬基本型別成員的大小所整除 0能被任意整數整除 2,結構體每個成員相對於結構體首位址的偏移量都是該成員型別大小的整數倍,如有需要編譯器會在成員之間加上填充位元組...

結構體位元組對齊

include pragma pack 2 struct t.pragma pack int main int argc,char argv 最後輸出的結果為 8。這個表示是按照2位元組來對齊資料,首先分配2位元組給成員變數i,分配完成後,還剩一位元組,zj add補0 沒法容納成員變數d,此時會再...