c 語言的一大優勢就是對記憶體空間的控制,當然,在物件導向語言的壓力下,程式設計師更喜歡輕鬆的語言,不喜歡自己還要顧慮記憶體空間。
可是,c 語言仍然有很強的生命力,尤其是在作業系統、嵌入式系統這兩方面,因為要直接操作硬體,c語言就顯現出自己強大的體制、機制、邏輯優勢。
c語言對記憶體控制,有乙個始終困擾初學者的問題:位元組對齊!
看一段程式:
[cpp]view plain
copy
struct
stexample
; //
sizeof
( char
) == 1
sizeof
( short
) == 2
sizeof
( int
) == 4
/sizeof
( struct
stexample ) == 8
stexample結構體的大小是8 byte. 看起來符合預期。
可是下面這個例子:
[cpp]view plain
copy
struct
stexample
; //
sizeof
( char
) == 1
sizeof
( short
) == 2
sizeof
( int
) == 4
/sizeof
( struct
stexample ) == 12
//?
stexample結構體的大小就變成了12 byte. 為什麼元素少了,反而占用空間會多出來4 byte? /
先讓我們看四個重要的基本概念:
1.資料型別自身的對齊值:
: 對於char型資料,其自身對齊值為1,對於short型為2,對於int,float,double型別,其自身對齊值為4,單位位元組。
2.結構體或者類的自身對齊值:
:其成員中自身對齊值最大的那個值。
3.指定對齊值:
:#pragma pack (value)時的指定對齊值value。
4.資料成員、結構體和類的有效對齊值:
:自身對齊值和指定對齊值中小的那個值。 /
有效對齊值n是最終用來決定資料存放位址方式的值,最重要。有效對齊n,就是 表示「對齊在n上」,也就是說該資料的"存放起始位址%n=0".
而資料結構中的資料變數都是按定義的先後順序來排放的。第乙個資料變數的起始位址就是資料結構的起始位址。結構體的成員變數要對齊排放。
例子分析:
[cpp]view plain
copy
struct
stexample
; //
sizeof
( char
) == 1
sizeof
( short
) == 2
sizeof
( int
) == 4
/sizeof
( struct
stexample ) == 12
//?
假設stexample從位址空間0x0000開始排放。該例子中沒有定義指定對齊值,在筆者環境下,該值預設為4。三個成員的儲存位置如圖:
1. 第乙個成員變數a的自身對齊值是1,比指定或者預設指定 對齊值4小,所以其有效對齊值為1,所以其存放位址0x0000符合0x0000%1=0.
2. 第二個成員變數b,其自身對齊值為4,所以有效對齊值也為4, 所以只能存放在起始位址為0x0004到0x0007這四個連續的位元組空間中,複核0x0004%4=0,且緊靠第乙個變數。
3. 第三個變數c,自身對齊值為 2,所以有效對齊值也是2,可以存放在0x0008到0x0009這兩個位元組空間中,符合0x0008%2=0。
所以從0x0000到0x0009存放的 都是stexample內容。再看資料結構stexample的自身對齊值為其變數中最大對齊值(這裡是b)所以就是4,所以結構體的有效對齊值也是4。根據結構體圓整的要求, 0x0009到0x0000=10位元組,(10+2)%4=0。所以0x0000a到0x000b也為結構體stexample所占用。故stexample從0x0000到0x000b 共有12個位元組,sizeof( struct stexample )=12.
其實如果就這乙個就來說它已將滿足位元組對齊了, 因為它的起始位址是0,因此肯定是對齊的,之所以在後面補充2個位元組,是因為編譯器為了實現結構陣列的訪問效率。
試想如果我們定義了乙個結構體stexample的陣列,那麼第乙個結構起始位址是0沒有問題,但是第二個結構呢?按照陣列的定義,陣列中所有元素都是緊挨著的,如果我們不把結構的大小補充為4的整數倍,那麼下乙個結構的起始位址將是0x0000a,這顯然不能滿足結構的位址對齊了,因此我們要把結構補充成有效對齊大小的整數倍.
其實諸如:對於char型資料,其自身對齊值為1,short型別為2,int,float,double型別,其自身對齊值為4,這些已有型別的自身對齊值也是基於陣列考慮的,只是因為這些型別的長度已知了,所以他們的自身對齊值也就已知了.
分析下面例子:
[cpp]view plain
copy
#pragma pack (2) /*指定按2位元組對齊*/
struct
stexample
; #pragma pack () /*取消指定對齊,恢復預設對齊*/
///sizeof
( struct
stexample ) == 8
1. 第 乙個變數a的自身對齊值為1,指定對齊值為2,所以,其有效對齊值為1,假設stexample 從0x0000開始,那麼a存放在0x0000,符合0x0000%1= 0;
2. 第二個變數b,自身對齊值為4,指定對齊值為2,所以有效對齊值為2,所以順序存放在0x0002、0x0003、0x0004、0x0005四個連續 位元組中,符合0x0002%2=0。
3. 第三個變數c的自身對齊值為2,所以有效對齊值為2,順序存放在0x0006、0x0007中,符合 0x0006%2=0。
所以從0x0000到0x00007共八字節存放的是stexample 的變數。又因為stexample 的自身對齊值為4,所以stexample 的有效對齊值為2。又8%2=0, stexample 只占用0x0000到0x0007的八個位元組。所以sizeof( struct stexample ) == 8.
//struct 結構體的巢狀結構也是類似的對齊方式。
看下面的例子:
[c-sharp]view plain
copy
struct
stexample
short
c;
};
//sizeof
( struct
stexample ) == 16
記憶體中的位元組對齊如下圖所示:
struct b 結構體的自身對齊值是4(由成員cc決定的),所以儲存位址必須是4的整數倍。
struct stexample 結構體的自身對齊值也是4(由成員struct b決定),所以最後兩個位元組用來補齊(即0x000e 和 0x0010)。
union 共用體的位元組對齊情況類似,共用體的自身對齊值決定於成員的最大自身對齊值。
位元組對齊,在一般情況下,在編寫上層應用程式時一般是不用顧慮的。
但是有兩種情況要特別小心,一是涉及到硬體memory操作,一是涉及到網路報文傳輸。
對網路報文定義結構體時,位元組不對齊的話就會造成大錯。有兩種方法解決:
1. 可以使用pack(1)宣告為1位元組對齊。但是操作效率會下降,而且有些嵌入式系統的編譯器支援不夠好。
2. 可以將網路報文結構體內的成員變數,定義時最大使用short型,可以使用char型,但要保持偶數位元組對齊。(一般標準的網路報文結構就是偶數位元組對齊的)。遇到需要int型的變數,可以定義乙個小共用體typedef union union_int ; 用它來代替int在報文結構體中使用,只是程式中注意點就行了。
**
union 與struct的空間計算
總體上遵循兩個原則 1 整體空間是 占用空間最大的成員 的型別 所佔位元組數的整倍數 2 資料對齊原則 記憶體按結構成員的先後順序排列,當排到該成員變數時,其前面已擺放的空間大小必須是該成員型別大小的整倍數,如果不夠則補齊,以此向後類推。注意 陣列按照單個變數乙個乙個的擺放,而不是看成整體。如果成員...
union 與struct的空間計算
一 x86 總體上遵循兩個原則 說明 假定結構體是從位址0開始依次存放各個變數的 struct s1 student 詳細解釋 sizeof 用法彙總 為什麼會有這樣的規定呢?這一定與處理器的字長有關 處理器一次訪問資料的寬度 和編譯器對結構體變數的處理有關。不幸的是,本人對x86架構不甚熟悉,只能...
union內嵌struct用法
union內嵌struct用法 眾所周知,union為聯合體,struct為結構體。下面根據例項談談用法 include include void main half number strcpy number.i,abcda printf c c n number.half.first,number...