詳解4位元組對齊

2021-08-03 09:54:40 字數 3080 閱讀 5757

摘要:

編譯器通常採用的預設位元組對齊規則:對於型別t,在n bit系統中,保證變數首位址在min(sizeof(t), n/8)位元組位置上,以保證最少讀週期。

以下為原文:

-----------------其實我也是一條分割線------------------

所謂的位元組對齊,就是各種型別的資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這個就是對齊。我們經常聽說的對齊在n上,它的含義就是資料的存放起始位址%n==0。具體對齊規則會在下面的篇幅中介紹。首先還是讓我們來看一下,為什麼要進行位元組對齊吧。

各個硬體平台對儲存空間的處理上有很大的不同。一些平台對某些特定型別的資料只能從某些特定位址開始訪問。比如有些架構的cpu,諸如sparc在訪問乙個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構上必須程式設計必須保證位元組對齊。而有些平台對於沒有進行對齊的資料進行訪問時會產生效率的下降。讓我們來以x86為例看一下如果在不進行對齊的情況下,會帶來什麼樣子的效率低下問題,看下面的資料結構宣告:

view plaincopy to clipboardprint?

01.struct a ;  

05.struct a a;  

code 13-1

假設變數a存放在記憶體中的起始位址為0x00,那麼其成員變數c的起始位址為0x00,成員變數i的起始位址為0x01,變數a一共占用了5個位元組。當cpu要對成員變數c進行訪問時,只需要乙個讀週期即可。而如若要對成員變數i進行訪問,那麼情況就變得有點複雜了,首先cpu用了乙個讀週期,從0x00處讀取了4個位元組(注意由於是32位架構),然後將0x01-0x03的3個位元組暫存,接著又花費了乙個讀週期讀取了從0x04-0x07的4位元組資料,將0x04這個位元組與剛剛暫存的3個位元組進行拼接從而讀取到成員變數i的值。為了讀取這個成員變數i,cpu花費了整整2個讀週期。試想一下,如果資料成員i的起始位址被放在了0x04處,那麼讀取其所花費的週期就變成了1,顯然引入位元組對齊可以避免讀取效率的下降,但這同時也浪費了3個位元組的空間(0x01-0x03)。

有了上述的基本概念之後,讓我們來看一下,編譯器是按照什麼樣的原則進行對齊的。首先有3個重要的概念:自身對齊值,指定對齊值和有效對齊值。

自身對齊值:即資料型別的自身的對齊值。例如char型的資料,其自身對齊值為1位元組;short型的資料,其自身對齊值為2位元組;int,float,long型別,其自身對齊值為4位元組;double型別,其自身對齊值為4位元組;而struct和class型別的資料其自身對齊值為其成員變數中自身對齊值最大的那個值。

指定對齊值:#pragma pack (value)時指定的對齊值value

有效對齊值:上述兩個對齊值中最小的那個。

我們一般說的對齊在n上,都是指有效對齊在n上。說了這麼多,還是讓我們先來看一些例子來加深對這些概念的理解吧。例:假設在x86機器上,假設編譯器按預設4位元組進行對齊

view plaincopy to clipboardprint?

01.struct a ;  

06.  

07.#pragma pack (2)   /* 指定按2位元組對齊 */  

08.struct b ;  

13.#pragma pack ()    /* 恢復預設對齊 */  

code 13-2

讓我們來考慮一下sizeof(struct a)和sizeof(struct b)的結果各應該是什麼。首先來看sizeof(struct a),假設a的起始位址為0x00,做這樣的假設只是為了更方便理解,其實a始終被放在對齊邊界上,這並不影響sizeof的結果,在接下來的例子中,我們也會繼續沿用這個假設。言歸正傳,資料成員c的自身對齊值=1,指定對齊值=4(預設),所以其有效對齊值為1,因0x00%1==0,所以它被存放在0x00處;資料成員i的自身對齊值=4,指定對齊值=4,可得出其有效對齊值為4,因0x01%4 != 0,因此它應該被存放在0x04位址處,占用0x05,0x06,0x07共4個位元組;接下來看資料成員s的自身對齊值=2,指定對齊值=4,得出有效對齊值為2,因0x08%2 == 0,因此它被存放在起始位址為0x08處,並占用2位元組;最後再看資料結構a自身的對齊值=4(最大資料成員自身對齊值),指定對齊值=4,得有效對齊值為4,因0x0a%4 != 0,因此多占用0x0a和0x0b為結構體a所用(這一步的作用是基於結構體陣列的出發,對於結構體或者類,要將它們補充成其有效對齊值的整數倍,這點請千萬注意)。由此可見sizeof(struct a)的結果應該是=1+3(空閒空間)+4+2+2(結構體補充)=12位元組。

接下來讓我們考察sizeof(struct b)的結果。這裡需要注意的是,在b被宣告前,指定對齊值已經被設定為2個位元組。資料成員c的有效對齊值為1,存放起址0x00,s的有效對齊值為2,存放起址0x02,i的有效對齊值也為2,存放起址為0x04,累加起來一共是8個位元組,已經是資料結構b的有效對齊值2的整數倍了。因此sizeof(struct b)的結果8個位元組。

看到這裡,應該對位元組對齊有了一定的了解了吧。接下來我們要看乙個更加複雜的例子:假設在x86機器上,假設編譯器按預設4位元組進行對齊 

view plaincopy to clipboardprint?

01.#pragma pack(8)  

02.struct s1 ;  

06.  

07.struct s2 ;  

12.#pragma pack()  

13.   

code 13-3

運用上面所學到的知識,應該不難得出sizeof(struct s1)的值為8位元組,其中a的有效對齊值為1,b的有效對齊值為4,結構s1的有效對齊值為4。現在讓我們來看看sizeof(struct s2)的值會是多少呢?首先成員c的有效對齊值為1,s1的自身對齊值為成員的最大自身對齊值,即4位元組,其指定對齊值為8,則其有效對齊值也為4,存放起址應該為0x04,並且占用8個位元組(0x04+0x08=0x0c),其中0x01-0x03被用來填充。接下去資料成員e的有效對齊值為4,存放起址應該是0x0d % 8 == 0,占用8個位元組(0x10)。最後考察s2本身的有效對齊值應該是4位元組,而0x0d%8==0,就不需要填充了,因此sizeof(struct s2)=20。

在vc6工具中,我們可以選擇[project]->[settings]->[c/c++]->[code generation]->[struct member alignment]來更改預設對齊位元組數。

參考:

詳解4位元組對齊

摘要 編譯器通常採用的預設位元組對齊規則 對於型別t,在n bit系統中,保證變數首位址在min sizeof t n 8 位元組位置上,以保證最少讀週期。以下為原文 其實我也是一條分割線 所謂的位元組對齊,就是各種型別的資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這個就是對齊。我...

詳解4位元組對齊

什麼是4位元組對齊?4位元組對齊有什麼好處,有什麼壞處?分析一波。首先先定義乙個結構體。typedef struct aa aa aa aa 32位處理器,如果沒有採用4位元組對齊,首先結構體變數aa存放在記憶體中的起始位址為0x00,那麼其成員變數a的起始位址為0x00,成員變數b的起始位址為0x...

位元組對齊 8位元組對齊

參考博文 參考1 參考2 參考3 在記憶體管理中經常使用位元組對齊來管理分配的記憶體。1 原理 2 演算法 2.1unsigned intcalc align unsigned int n,unsigned align 2.2 更好的演算法 unsigned intcalc align unsign...