一、啥是字對齊?為啥要字對齊?
現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問都可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體位址訪問,這就是對齊。
位元組對齊的原因大致是如下兩條:
1、平台原因(移植原因):不是所有的硬體平台都能訪問任意位址上的任意資料的;某些硬體平台只能在某些位址處取某些特定型別的資料,否則丟擲硬體異常。
2、效能原因:資料結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問。
二、對齊規則
每個特定平台上的編譯器都有自己的預設「對齊係數」(也叫對齊模數)。程式設計師可以通過預編譯命令#pragma pack(n),n=1,2,4,8,16來改變這一係數,其中的n就是你要指定的「對齊係數」。
規則:1. 資料成員對齊規則:結構(struct)(或聯合(union))的資料成員,第乙個資料成員放在offset為0的地方,以後每個資料成員的對齊按照#pragma pack指定的數值和這個資料成員自身長度中,比較小的那個進行。
2. 結構(或聯合)的整體對齊規則:在資料成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大資料成員長度中,比較小的那個進行。
3. 結合1、2可推斷:第
一、如果n大於等於該變數所占用的位元組數,那麼偏移量必須滿足預設的對齊方式,第
二、如果n小於該變數的型別所占用的位元組數,那麼偏移量為n的倍數,不用滿足預設的對齊方式。
三、x86對齊實驗
下面再簡要回顧解釋一下上述的對齊規則,結合例項進行分析:
1. 資料型別自身的對齊值:對於char型資料,其自身對齊值為1位元組,對於short型為2位元組,對於int,float,double型別,其自身對齊值為4位元組。
2. 結構體的自身對齊值:其成員中自身對齊值最大的那個值。
3. 指定對齊值:#pragma pack(n)來設定變數以n位元組對齊方式。n位元組對齊就是說變數存放的起始位址的偏移量有兩種情況,第
一、如果n大於等於該變數所占用的位元組數,那麼偏移量必須滿足預設的對齊方式,第
二、如果n小於該變數的型別所占用的位元組數,那麼偏移量為n的倍數,不用滿足預設的對齊方式。
4. 資料成員和結構體的有效對齊值:資料成員(資料型別)和資料結構的自身對齊值和指定對齊值中小的那個值,資料成員對齊了資料結構自然也就對齊了。
了解上述四個基本概念,我們開始討論具體資料結構的成員和其自身的對齊方式。有效對齊值n是最終用來決定資料存放位址方式的值。有效對齊n,就是表示「對齊在n上」,也就是說該資料的"存放起始位址%n=0"。而資料結構中的資料變數都是按定義的先後順序來排放的。第乙個資料變數的起始位址就是資料結構的起始位址。結構體的成員變數要對齊排放,結構體本身也要根據自身的有效對齊值圓整(結構體成員變數占用總長度需要是對結構體有效對齊值的整數倍)。下面結合vs2005中編譯環境的例子進行深入了解:
例子b分析:
struct b
;假設b從位址空間0x0000開始排放。該例中沒有顯式指定對齊值n,vs2005預設值為4。
成員變數b自身對齊值是1,比指定或缺省指定對齊值4小,故有效對齊值為1,其存放位址0x0000符合0x0000%1=0,滿足位元組對齊原則。
成員變數a自身對齊值為4,和指定或缺省指定對齊值4相等,故有效對齊值也為4,為了保證位元組對齊,成員變數a只能存放在起始位址為0x0004到0x0007這四個連續的位元組空間中,複核0x0004%4=0。
成員變數c自身對齊值為2,比指定或缺省指定對齊值4小,故有效對齊值為2,可順序存放在0x0008至0x0009兩個位元組空間中,符合0x0008%2=0。
至此滿足了資料成員的位元組對齊,接著看資料結構b的對齊。資料結構b的自身對齊值為其變數中最大對齊值(也就是成員變數b)4,故結構體b的有效對齊值也是4。根據結構體圓整的要求, 0x0009到0x0000=10位元組,(10+2)%4=0。所以0x0000a到0x000b也為結構體b所占用。故b從0x0000到0x000b 共有12個位元組,sizeof(struct b)=12。
之所以在變數c補充2位元組,是因為要實現編譯器快速有效的訪問結構陣列,試想如果定義b結構陣列,第乙個結構起始位址是0沒有問題,但是第二個結構呢?按照陣列的定義,陣列中所有元素都是緊挨著的,如果不把結構的大小補充為對齊值(4)的整數倍,那下乙個結構的起始位址將是0x0000a,這顯然不能滿足結構的位址對齊了。
例子c分析:
/*指定按2位元組對齊*/
__align(2) struct c
;/*取消指定對齊,恢復預設對齊*/
同理,例子c中成員變數b自身對齊值為1,指定對齊值為2,故效對齊值為1,假設c從0x0000開始,那麼b存放在0x0000,符合0x0000%1= 0,滿足位元組對齊原則。
成員變數a自身對齊值為4,指定對齊值為2,故有效對齊值為2,順序存放在0x0002、0x0003、0x0004、0x0005四個連續位元組中,符合0x0002%2=0,滿足位元組對齊原則。
成員變數c的自身對齊值為2,與指定對齊值相等,故有效對齊值為2,順序存放在0x0006、0x0007中,符合 0x0006%2=0,滿足位元組對齊原則。
從0x0000到0x00007共八字節存放的是結構體c的變數。結構體c自身對齊值為4,比指定對齊值2大,故c的有效對齊值為2,因8%2=0,c只占用0x0000到0x0007的八個位元組。所以sizeof(struct c)=8,完全滿足位元組對齊原則。
除了指定的對齊值不同能導致資料結構的位址存放不同外, 編譯器不同存放結構體方式也可能不同。
四、arm平台的對齊問題
在arm中,有arm和thumb兩種指令。
arm指令
:每執行一條指令,pc的值加4個位元組(32bits).一次訪問4位元組內容,該位元組的起始位址必須是4位元組對齊的位置上,即位址的低兩位為bits[0b00],也就是說位址必須是4的倍數。
thumb指令
:每執行一條指令,pc的值加2個位元組(16bits).).一次訪問2位元組內容,該位元組的起始位址必須是2位元組對齊的位置上,即位址的低兩位為bits[0b0],也就是說位址必須是2的倍數。
遵循以上方式叫對齊(aligned)方式,不遵守這樣方式稱為非對齊(unaligned)的儲存訪問操作。
五、arm平台位元組對齊關鍵字
1. __align(num)
用於修改最高端別物件的位元組邊界。
a、在彙編中使用ldrd或者strd時,就用到此命令__align(8)進行修飾限制。來保證資料物件是相應對齊。
b、該修飾物件的命令最大是8個位元組限制,可讓2位元組的物件進行4位元組
對齊,但是不能讓4位元組的物件2位元組對齊。
c、 __align是儲存類修改,他只修飾最高端型別物件不能用於結構或者函式物件。
2. __packed
__packed是進行一位元組對齊。
a、不能對packed的物件進行對齊;
b、所有物件的讀寫訪問都進行非對齊訪問;
c、float及包含float的結構聯合及未用__packed的物件將不能位元組對齊;
d、__packed對區域性整形變數無影響;
d、強制由unpacked物件向packed物件轉化是未定義,整形指標可以合法定
義為packed __packed int* p; //__packed int 則沒有意義。
3. __unaligned
用於修飾該變數可按照非對齊訪問。
六、如何查詢與位元組對齊方面的問題
如果出現對齊或者賦值問題首先檢視:
1. 編譯器的big little端設定;
2. 看這種體系本身是否支援非對齊訪問;
3. 如果支援看設定了對齊與否,如果沒有則看訪問時需要加某些特殊的修飾來標誌其特殊訪問操作。
七、結論
針對於32位處理器對於本地使用的資料結構,為提高記憶體訪問效率,採用四位元組對齊方式;同時為了減少記憶體的開銷,合理安排結構成員的位置,減少四位元組對齊導致的成員之間的空隙,降低記憶體開銷。
對於處理器之間的資料結構,需要保證訊息的長度不因為在不同編譯平台和不同處理器導致訊息結構的長度發生變化,使用一位元組對齊方式對訊息結構進行緊縮;為保證處理器之間的訊息的資料結構的記憶體訪問效率,採用位元組填充的方式自己對訊息中成員進行四位元組對齊。
資料結構的成員位置要兼顧成員之間的關係、資料訪問效率和空間利用率。順序安排的原則是:四位元組的放在最前面,兩位元組的緊接最後乙個四位元組成員,一位元組緊接最後乙個兩位元組成員,填充位元組放在最後。舉例如下:
typedef struct tag_t_msg t_msg;
ARM字對齊及關鍵字
一 為什麼要字對齊?現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問都可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體位址訪問,這就是對齊。位元組對齊的原因大致是如下兩條 1 平台原因 移植原因 不是所有的硬體平台都能訪問任意位址上的任意資...
字對齊 半字對齊 位元組對齊的理解
一般情況下字為32位 4位元組 半字為16位 2位元組 位元組為8位 1位元組 大多數計算機使用 位元組 8位的資料塊 作為最小可定址的儲存器單位 而不是訪問儲存器中單獨的位。儲存器的每乙個位元組都由唯一的數字標識,稱為該位元組的位址,所有可能位址的集合稱為儲存器空間。舉例來說,arm處理器工作狀態...
字對齊 半字對齊 位元組對齊的理解
一般情況下字為32位 4位元組 半字為16位 2位元組 位元組為8位 1位元組 大多數計算機使用位元組 8位的資料塊 作為最小可定址的儲存器單位,而不是訪問儲存器中單獨的位。儲存器的每乙個位元組都由唯一的數字標識,稱為該位元組的位址,所有可能位址的集合稱為儲存器空間。舉例來說,arm處理器工作狀態有...