我們看如下列子,並考慮結構體每個成員屬性偏移
struct mystruct
;
然後我們檢視記憶體結構:
我們驚訝的差異在name
之後空出了3個位元組記憶體cc
,而在gender
到height
記憶體結構中同樣也多出了3個位元組的cc
.乙個多出了6個空白位元組,那麼加上固有的26位元組那麼這個結構體共占用32
位元組。
造成這樣的現象是由於c存在結構體對齊機制引起的,在編譯引數由zp1,zp2,zp4,zp8,zp16
控制。比如zp1
為對齊大小為1,zp8
為8位元組對齊。預設為zp8
。
上述的例子如果想輸出26只需要修改編譯引數為zp1
即可。
結構體對齊作用(1):
控制的是每個成員屬性對於結構體初始位址的偏移
。
假設結構體對齊大小為n,結構體成員為y,結構體初始偏移為x,那麼偏移必須為x%min(n,sizeof(y))==0
。(其中min函式表示取兩個數最小值)
以本例為例:
構造乙個mystruct
物件名為person
,我們強制假設person
的位址是0.那麼我來計算這個結構體中的每個成員的偏移量。
struct mystruct
;void
main
(int argc,
char
*ar**,
char
*envp)
我們用假設對齊大小為8。我們看第乙個結構體成員
char name[9];
。由於在結構體第一位所以偏移位址為0
,由於0%min(8,9)==0
所以name
偏移為0。
我們看第二個結構體成員age
。由於第乙個結構體成員name
大小為9
,所以age
偏移量為9
,我們取模運算9%min(8,4)==1
,不符合對齊大小,所增大12
可以滿足12%min(8,4)==0
,故偏移為12
.
後面成員模擬不做講解。
當然我們可以使用系統的api來幫助我們迅速計算成員偏移.
struct mystruct
;void main(int argc,char *ar**,char *envp)
offsetof實現是乙個巨集函式,思想比較簡單,構造乙個執行0位址的mystruct,對成員取位址即可
#ifdef __cplusplus
#define offsetof(s,m) ((size_t)&reinterpret_cast((((s*)0)->m)))
#else
#define offsetof(s,m) ((size_t)&(((s*)0)->m))
struct newmystruct
;void
main
(int argc,
char
*ar**,
char
*envp)
根據上乙個章節講解的每個成員的偏移計算,height
偏移為24
那麼這個結構體大小應該是24+4=28
(由於float
大小為4
,所最後乙個成員的偏移加上大小理所當然為結構體大小)
然而結果輸出:32
結構體對齊作用(2):
結構體對齊控制整個結構體的大小。
結構體的大小公式:
設結構體每個成員為 m1,m2,m3…
對齊大小為n
結構體大小為y
temp=min(n,max(sizof(m1),sizeof(m2),sizeof(m3),....))
y%temp==0
帶入本例
struct newmystruct
;
temp = min(8,max(5,4,1,8,4)) = 8
此時我們知道這個結構體至少大小為28,而28%8==4
,不滿足y%temp==0
,所以size擴大為32。
除了使用zp1,zp2,zp4,zp8,zp16
控制全域性程式結構體大小。還可以使用編譯巨集指令單獨對某個某個結構體,或對後續全部的結構體對齊指定。
1)#pragma pack( [ n] )
2)#pragma pack( [ [ , ] [ identifier, ] ] [ n ] )
#pragma pack( [ n] ) 用法
這個編譯巨集後面所有的全部對齊為指定數量
//後續所有結構對其為1
//對齊為1
#pragma pack(1)
struct mystruct
;//對齊為1
struct mystruct2
;#pragma pack(4)
//對齊為4
struct mystruct3
;
如果你想控制範圍請用如下語法:
//儲存當前結構體巨集對齊大小
#pragma pack(push)
#pragma pack(1)
//對齊為1
struct mystruct
;//彈出儲存的結構體的巨集對齊大小
#pragma pack(pop)
//對齊為預設
struct mystruct2
;
第乙個引數 push pop 用於 儲存或者彈出巨集大小。
第二個引數 設定乙個名詞
第三個引數 對齊大小
//儲存當前對其大小,並設定對齊大小為1
#pragma pack(push,myaligment,1)
//對齊為1
struct mystruct
;//還原儲存的對其大小
#pragma pack(pop,myaligment)
//對齊為預設
struct mystruct2
;
這個編譯指令主要用於巢狀用途。
//儲存當前對其大小,並設定對齊大小為1
#pragma pack(push,myaligment,1)
//對齊為1
struct mystruct
;//儲存當前對其大小,並設定對齊大小為4
#pragma pack(push,myaligment2,4)
//對齊為4
struct mystruct2
;//還原儲存的對其大小由於myaligment2巢狀在myaligment,
//所以會直接拋棄myaligment2和myaligment
#pragma pack(pop,myaligment)
//對齊為預設
struct mystruct3
;
//儲存當前對其大小,並設定對齊大小為1
#pragma pack(push,myaligment,1)
//對齊為1
struct mystruct
;//儲存當前對其大小,並設定對齊大小為4
#pragma pack(push,myaligment2,4)
//對齊為4
struct mystruct2
;//只拋棄對齊4,但是myaligment還在
#pragma pack(pop,myaligment2)
//對齊為1
struct mystruct3
;
結構體對齊計算方式
結構體的大小也不是成員型別大小的簡單相加。需要考慮到系統在儲存結構體變數時的位址對齊問題。由於儲存變數位址對齊的問題,結構體大小計算必須滿足兩條原則 一 結構體成員的偏移量必須是成員大小的整數倍 0被認為是任何數的整數倍 二 結構體大小必須是所有成員大小的整數倍 陣列除外,結構體中的結構體按單個變數...
c 結構體對齊
1 當前偏移量 當前填充數 當前變數大小 0 2 總偏移大小 末尾填充數 最寬變數大小 0 必須先滿足1 再滿足2。例如 如下 struct a 由上可知 總偏移量 4 1 1 2 末尾補數0 最寬變數大小4 0。所以sizeof a 8。即4 1 1 2 8 快速記憶 4 1 1 2 8 stru...
C語言 結構體struct 結構體對齊
1 定義乙個結構體 順便例項結構體變數 struct tag 結構體型別名 struct tag 這兩者共同構成了結構體型別 單獨的tag 結構體型別名 不能稱之為結構體型別 結構體變數名 2 定義的同時使用typedef 相當於定義結構體 為結構體起新名字 typedef struct tag 結...