什麼是位元組對齊,為什麼要對齊?
現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體地址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。
對齊的作用和原因:各個硬體平臺對儲存空間的處理上有很大的不同。一些平臺對某些特定型別的資料只能從某些特定地址開始存取。比如有些架構的cpu在訪問一個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下程式設計必須保證位元組對齊.其他平臺可能沒有這種情況,但是最常見的是如果不按照適合其平臺要求對資料存放進行對齊,會在存取效率上帶來損失。比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那麼一個讀週期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit資料。顯然在讀取效率上下降很多。
結構的儲存分配
編譯器按照結構體成員列表的順序為每個成員分配記憶體,當儲存成員時需要滿足正確地邊界對齊要求時,成員之間可能出現用於填充地額外記憶體空間。32位系統每次分配位元組數最多為4個位元組,64位系統分配位元組數最多為8個位元組。
以下圖表是在不同系統中基本型別資料記憶體大小和預設對齊模數:
注:此外指標所佔記憶體的長度由系統決定,在32位系統下為32位(即4個位元組),64位系統下則為64位(即8個位元組).
沒有#pragma pack巨集的對齊
對齊規則:
結構體的起始儲存位置必須是能夠被該結構體中最大的資料型別所整除。
每個資料成員儲存的起始位置是自身大小的整數倍(比如int在32位機為4位元組,則int型成員要從4的整數倍地址開始儲存)。
結構體總大小(也就是sizeof的結果),必須是該結構體成員中最大的對齊模數的整數倍。若不滿足,會根據需要自動填充空缺的位元組。
結構體包含另一個結構體成員,則被包含的結構體成員要從其原始結構體內部最大對齊模數的整數倍地址開始儲存。(比如struct a裡存有struct b,b裡有char,int,double等元素,那b應該從8的整數倍開始儲存。)
結構體包含陣列成員,比如char a[3],它的對齊方式和分別寫3個char是一樣的,也就是說它還是按一個位元組對齊。如果寫:typedef char array[3],array這種型別的對齊方式還是按一個位元組對齊,而不是按它的長度3對齊。
結構體包含共用體成員,則該共用體成員要從其原始共用體內部最大對齊模數的整數倍地址開始儲存。
現在給出一個結構體,我們針對win-32和linux-32進行分析,
例1:struct mystruct
;解答:win-32位系統下:
由上圖可知該結構體的最大對齊模數為sizeof(long double)=8;假設mystruct從地址空間0x0000開始存放。char為1個位元組,所以a存放於0x0000中;int為4個位元組,根據規則,b儲存的起始地址必須為其對齊模數4的整數倍,所以a後面自動填充空缺位元組空間0x0001-0x0003,因此b存放於0x0004-0x0007中。long double是8個位元組,由於32位系統每次最多分配4個位元組,則首先分配0x0008-0x000b,由於不夠儲存空間,則繼續分配0x000c-0x000f,所以c儲存在0x0008-0x000f中,由於此時總儲存空間為4+4+8=16;則16滿足最大對齊模數sizeof(long double)=8的整數倍;因此,sizeof(mystruct)=16個位元組。
linux-32位系統下:
由上圖可知該結構體的最大對齊模數為4;假設mystruct從地址空間0x0000開始存放。char為1個位元組,所以a存放於0x0000中;int為4個位元組,根據規則,b儲存的起始地址必須為其對齊模數4的整數倍,所以a後面自動填充空缺位元組空間0x0001-0x0003,因此b存放於0x0004-0x0007中。long double是12個位元組,由於32位系統每次最多分配4個位元組,則首先分配0x0008-0x000b,由於不夠儲存空間,則繼續分配0x000c-0x000f,仍然不滿足儲存c,則繼續分配0x0010-0x0013,所以c儲存在0x0www.cppcns.com008-0x0013中,由於此時總儲存空間為4+4+12=20;則20滿足最大對齊模數4的整數倍;因此,sizeof(mystruct)=20個位元組。
注:以下的所有例子都是在win-32下實現
例2:struct b;
由上圖可知該結構體的最大對齊模數為sizeof(int)=4;假設b從地址空間0x0000開始存放。char為1個位元組,所以a存放於0x0000中;int為4個位元組,根據規則,b儲存的起始地址必須為其對齊模數4的整數倍,所以a後面自動填充空缺位元組空間0x0001-0x0003,因此b存放於0x0004-0x0007中。c也是char型別,所以c存放在0x0008中;此時結構體b總的大小為4+4+1=9個位元組;則9不能滿足最大對齊模數4的整數倍;因此在c的後面自動填充空間0x0009-0x000b,使其滿足最大對齊模數的倍數,最終結構體b的儲存空間為0x0000-0x000b;則sizeof(b)=12個位元組。
例3:空結構體
struct c;
sizeof(c) = 0或sizeof(c);
c為空結構體,在c語言中佔0位元組,在c++中佔1位元組。
例4:結構體有靜態成員
struct d;
靜態成員變數存放在全域性資料區內,在編譯的時候已經分配好記憶體空間,所以對結構體的總記憶體大小不做任何貢獻;因此,sizeof(d)=4+4=8個位元組
例5:結構體中包含結構體
struct e;
struct f;
在結構體e中最大對齊模數是sizeof(double)=8;且sizeof(e)=8+8+8=24個位元組;在結構體f中,除了結構體成員e之外,其他的最大對齊模數是sizeof(int)=4;又因為結構體e中最大對齊模數是sizeof(double)=8;所以結構體f的最大對齊模數取e的最大對齊模數8;因此,sizeof(f)=4+4+8+24=40個位元組。
例6:結構體包含共用體
union union1
; struct e;
共用體中的最大對齊模式是sizeof(double)=8;則sizeof(union1)=16;結構體e的最大對齊模數也是8;則sizeof(e)=8+8+8+16=40個位元組。
例7:結構體包含指標成員
typedef struct aa;
結構體包含的指標成員的大小根據系統型別決定,由於這裡是在win-32位系統下分析,則指標大小為4個位元組;因此,結構體a的最大對齊模數為sizeof(double)=8;則sizeof(a)=4+4+8+8+4+4+8=40個位元組。
存在#pragma pack巨集的對齊
#pragma pack (n) //編譯器將按照n個位元組對齊
#pragma pack () //取消自定義位元組對齊方式
對齊規則:
結構,聯合,或者類的資料成員,第一個放在偏移為0的地方,以後每個資料成員的對齊,按照#pragma pack指定的數值和自身對齊模數中較小的那個。
例8:按指定的對齊模數
#pragma pack (2) /*指定按2位元組對齊*/
struct g;
#pragma pack () /*取消指定對齊,恢復預設對齊*/
在結構體g中成員變數的最大對齊模數是sizeof(double)=8;又因為指定對齊模數是2;所以取其較小者2為結構體g的最大對齊模數;則sizeof(oatgdg)=2+4+8+2=16;由於16是2的整數倍,則不需要填充。
總結在分析結構體位元組對齊時,首先確定有沒有利用#pragma pack()巨集定義指定對齊模數;根據情況對應上面進行兩種情況分析,針對不同的系統會得到不同的結果。
補充:oatgd在visual c++下可以用__declspec(align(#))宣告資料按#位元組對齊
gun c下可以使用以下命令:
__attribute__((aligned (n))),讓所作用的結構成員對齊在n位元組自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊
__attribute__((__packed__)),取消結構在編譯過程中的優化對齊,按照實際佔用位元組數進行對齊。
c++11新加關鍵字alignas(n)
本文標題: 深入剖析c++中的struct結構體位元組對齊
本文地址: /ruanjian/c/150590.html
總結 C 與C 中的struct結構體
c 結構體 支援繼承,甚至可以多繼承,虛繼承,多型,過載運算子,模板,定義友元函式,友元類等。c 結構體和類區別 struct b a。繼承預設是public的,而class預設是private的 你可以將程式裡所有的class全部替換成struct,它依舊可以很正常的執行 結構體物件所佔的空間大小...
C 中的結構型別(struct)
有時候,類中只包含極少的資料,因為管理堆而造成的開銷顯得極不合算。這種情況下,更好的做法是使用結構 struct 型別。由於 struct 是值型別,是在棧 stack 上儲存的,所以能有效的減少記憶體管理的開銷 當然前提是這個結構足夠小 結構可以包含它自己的欄位 方法和構造器。int 實際上是 s...
C 中結構 struct 的部分初始化和完全初始化
假設有這樣一個值型別struct。public struct size 客戶端,給所有struct欄位初始化後呼叫方法 class program 結果 50 客戶端,給部分struct欄位初始化 class program 結果 報錯,使用了未賦值的區域性變數。可見 如果想呼叫struct例項的任...
詳說C 中的結構struct
一 結構和類的區別 1 結構的級別和類一致,寫在名稱空間下面,可以定義欄位 屬性 方法 構造方法也可以通過關鍵字new建立物件。2 結構中的欄位不能賦初始值。3 無引數的建構函式無論如何c 編譯器都會自動生成,所以不能為結構定義一個無參建構函式。4 在建構函式中,必須給結構體的所有欄位賦值。5 在建...
詳說C 中的結構struct
一 結構和類的區別 1 結構的級別和類一致,寫在名稱空間下面,可以定義欄位 屬性 方法 構造方法也可以通過關鍵字new建立物件。2 結構中的欄位不能賦初始值。3 無引數的建構函式無論如何c 編譯器都會自動生成,所以不能為結構定義一個無參建構函式。4 在建構函式中,必須給結構體的所有欄位賦值。5 在建...
深入剖析C 中的string類
comments 本文摘自於網路 一,c語言的字串 在c語言裡,對字串的處理一項都是一件比較 痛苦的事情,因為通常在實現字串的操作的時候都會用到最不容易駕馭的型別 指標。比如下面這個例子 example 1 char str 12 hello char p str p h 改變第一個字母 examp...