在c語言中,結構是一種復合資料型別,其構成元素既可以是基本資料型別(如int、long、float等)的變數,也可以是一些復合資料型別(如陣列、結構、聯合等)的資料單元。在結構中,編譯器為結構的每個成員按其自然對界(alignment)條件分配空間
。各個成員按照它們被宣告的順序在記憶體中順序儲存,第乙個成員的位址和整個結構的位址相同。例如,下面的結構各成員空間分配情況:
struct test
;結構的第乙個成員x1為char型別,其自然對界為1,其偏移位址為0,佔據了第1個位元組。第二個成員x2為short型別,其自然對界為2,起始位址必須2位元組對界,因此,編譯器在x2和x1之間填充了乙個空位元組。結構的第三個成員x3和第四個成員x4恰好落在其自然對界位址上,在它們前面不需要額外的填充位元組。在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大對界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。
更改c編譯器的預設位元組對齊方式
在預設情況下,c編譯器為每乙個變數或是資料單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變預設的對界條件:
· 使用偽指令#pragma pack (n),c編譯器將按照n個位元組對齊。
· 使用偽指令#pragma pack (),取消自定義位元組對齊方式。
另外,還有如下的一種方式:
· __attribute((aligned (n))),讓所作用的結構成員對齊在n位元組自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。
· __attribute__ ((packed)),取消結構在編譯過程中的優化對齊,按照實際占用位元組數進行對齊。
以上的n = 1, 2, 4, 8, 16... 第一種方式較為常見。
應用例項
在網路協議程式設計中,經常會處理不同協議的資料報文。一種方法是通過指標偏移的方法來得到各種資訊,但這樣做不僅程式設計複雜,而且一旦協議有變化,程式修改起來也比較麻煩。在了解了編譯器對結構空間的分配原則之後,我們完全可以利用這一特性定義自己的協議結構,通過訪問結構的成員來獲取各種資訊。這樣做,不僅簡化了程式設計,而且即使協議發生變化,我們也只需修改協議結構的定義即可,其它程式無需修改,省時省力。下面以tcp協議首部為例,說明如何定義協議結構。其協議結構定義如下:
#pragma pack(1) // 按照1位元組方式進行對齊
struct tcpheader
; #pragma pack() // 取消1位元組對齊方式
下面有一道在 csdn論壇 上討論火熱的題:
#pragma pack(8)
struct s1;
struct s2;
#pragma pack()
問1.sizeof(s2) = ?
2.s2的c後面空了幾個位元組接著是d?
結果如下:
sizeof(s2)結果為24.
成員對齊有乙個重要的條件,即每個成員分別對齊.即每個成員按自己的方式對齊.
也就是說上面雖然指定了按8位元組對齊,但並不是所有的成員都是以8位元組對齊.其對齊的規則是,每個成員按其型別的對齊引數(通常是這個型別的大小)和指定對齊引數(這裡是8位元組)中較小的乙個對齊.並且結構的長度必須為所用過的所有對齊引數的整數倍,不夠就補空位元組.
s1中,成員a是1位元組預設按1位元組對齊,指定對齊引數為8,這兩個值中取1,a按1位元組對齊;成員b是4個位元組,預設是按4位元組對齊,這時就按4位元組對齊,所以sizeof(s1)應該為8;
s2 中,c和s1中的a一樣,按1位元組對齊,而d 是個結構,它是8個位元組,它按什麼對齊呢?對於結構來說,它的預設對齊方式就是它的所有成員使用的對齊引數中最大的乙個,s1的就是4.所以,成員d就是按4位元組對齊.成員e是8個位元組,它是預設按8位元組對齊,和指定的一樣,所以它對到8位元組的邊界上,這時,已經使用了12個位元組了,所以又新增了4個位元組的空,從第16個位元組開始放置成員e.這時,長度為24,已經可以被8(成員e按8位元組對齊)整除.這樣,一共使用了24個位元組.
a b
s1的記憶體布局:11**,1111,
c s1.a s1.b d
s2的記憶體布局:1***,11**,1111,****11111111
這裡有三點很重要:
1.每個成員分別按自己的方式對齊,並能最小化長度
2.複雜型別(如結構)的預設對齊方式是它最長的成員的對齊方式,這樣在成員是複雜型別時,可以最小化長度
3.對齊後的長度必須是成員中最大的對齊引數的整數倍,這樣在處理陣列時可以保證每一項都邊界對齊
補充一下,對於陣列,對齊方式為元素的對齊方式。比如:
char a[3];這種,它的對齊方式和分別寫3個char是一樣的.也就是說它還是按1個位元組對齊.
如果寫: typedef char array3[3];
array3這種型別的對齊方式還是按1個位元組對齊,而不是按它的長度.
不論型別是什麼,對齊的邊界一定是1,2,4,8,16,32,64....中的乙個.
struct s1
;struct s2
;struct s3
;struct s4
;cout<union的對齊方式:為成員中最大的對齊方式,長度為按照這個對齊方式調整最長成員得到的長度。
union u
;union u2
;union u3
;cout<
結論:復合資料型別,如union,struct,class的對齊方式為成員中對齊方式最大的成員的對齊方式。
順便提一下cpu對界問題,32的c++採用8位對界來提高執行速度,所以編譯器會盡量把資料放在它的對界上以提高記憶體命中率。對界是可以更改的,使用#pragmapack(x)巨集可以改變編譯器的對界方式,預設是8。c++固有型別的對界取編譯器對界方式與自身大小中較小的乙個。例如,指定編譯器按2對界,int型別的大小是4,則int的對界為2和4中較小的2。在預設的對界方式下,因為幾乎所有的資料型別都不大於預設的對界方式8(除了 longdouble),所以所有的固有型別的對界方式可以認為就是型別自身的大小。 www. 更改一下上面的程式:
#pragmapack(2)
union u2
;union u3
;#pragmapack(8)
cout<
C C 中的記憶體補齊機制
原文 在c語言中,結構是一種復合資料型別,其構成元素既可以是基本資料型別 如int long float等 的變數,也可以是一些復合資料型別 如陣列 結構 聯合等 的資料單元。在結構中,編譯器為結構的每個成員按其自然對界 alignment 條件分配空間。各個成員按照它們被宣告的順序在記憶體中順序儲...
C C 中的記憶體補齊機制
在c語言中,結構是一種復合資料型別,其構成元素既可以是基本資料型別 如int long float等 的變數,也可以是一些復合資料型別 如陣列 結構 聯合等 的資料單元。在結構中,編譯器為結構的每個成員按其自然對界 alignment 條件分配空間。各個成員按照它們被宣告的順序在記憶體中順序儲存,第...
C C 中的記憶體補齊機制
今天做了道牛客的題,錯了。之前沒有接觸過記憶體補齊的問題。題目如下 在x86系統下,sizeof如下結構體的值是多少?12 345struct 答案是c 在c語言中,結構是一種復合資料型別,其構成元素既可以是基本資料型別 如int long float等 的變數,也可以是一些復合資料型別 如陣列 結...