文章最後本人做了一幅圖,一看就明白了,這個問題網上講的不少,但是都沒有把問題說透。
__attribute__((packed))得變數或者結構體成員使用最小的對齊方式,即對變數是一位元組對齊,對域(field)是位對齊.
五、什麼時候需要設定對齊
在設計不同cpu下的通訊協議時,或者編寫硬體驅動程式時暫存器的結構這兩個地方都需要按一位元組對齊。即使看起來本來就自然對齊的也要使其對齊,以免不同的編譯器生成的**不一樣.
一、快速理解
1. 什麼是位元組對齊?
在c語言中,結構是一種復合資料型別,其構成元素既可以是基本資料型別(如int、long、float等)的變數,也可以是一些復合資料型別(如陣列、結構、聯合等)的資料單元。在結構中,編譯器為結構的每個成員按其自然邊界(alignment)分配空間。各個成員按照它們被宣告的順序在記憶體中順序儲存,第乙個成員的位址和整個結構的位址相同。
為了使cpu能夠對變數進行快速的訪問,變數的起始位址應該具有某些特性,即所謂的」對齊」. 比如4位元組的int型,其起始位址應該位於4位元組的邊界上,即起始位址能夠被4整除.
2. 位元組對齊有什麼作用?
位元組對齊的作用不僅是便於cpu快速訪問,同時合理的利用位元組對齊可以有效地節省儲存空間。
對於32位機來說,4位元組對齊能夠使cpu訪問速度提高,比如說乙個long型別的變數,如果跨越了4位元組邊界儲存,那麼cpu要讀取兩次,這樣效率就低了。但是在32位機中使用1位元組或者2位元組對齊,反而會使變數訪問速度降低。所以這要考慮處理器型別,另外還得考慮編譯器的型別。在vc中預設是4位元組對齊的,gnu gcc 也是預設4位元組對齊。
3. 更改c編譯器的預設位元組對齊方式
在預設情況下,c編譯器為每乙個變數或是資料單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變預設的對界條件:
· 使用偽指令#pragma pack (n),c編譯器將按照n個位元組對齊。
· 使用偽指令#pragma pack (),取消自定義位元組對齊方式。
另外,還有如下的一種方式:
· __attribute((aligned (n))),讓所作用的結構成員對齊在n位元組自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。
· __attribute__ ((packed)),取消結構在編譯過程中的優化對齊,按照實際占用位元組數進行對齊。
4. 舉例說明
例1struct test;
由於編譯器預設情況下會對這個struct作自然邊界(有人說「自然對界」我覺得邊界更順口)對齊,結構的第乙個成員x1,其偏移位址為0,佔據了第1個位元組。第二個成員x2為short型別,其起始位址必須2位元組對界,因此,編譯器在x2和x1之間填充了乙個空位元組。結構的第三個成員x3和第四個成員x4恰好落在其自然邊界位址上,在它們前面不需要額外的填充位元組。在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大邊界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。例2
#pragma pack(1) //讓編譯器對這個結構作1位元組對齊
struct test
;#pragma pack() //取消1位元組對齊,恢復為預設4位元組對齊
這時候sizeof(struct test)的值為8。
例3#define gnuc_packed __attribute__((packed))
struct packed test
gnuc_packed;
這時候sizeof(struct test)的值仍為8。
二、深入理解
三.編譯器是按照什麼樣的原則進行對齊的?
四.如何修改編譯器的預設對齊值?
1.在vc ide中,可以這樣修改:[project]|[settings],c/c++選項卡category的code generation選項的struct member alignment中修改,預設是8位元組。
2.在編碼時,可以這樣動態修改:#pragma pack .注意:是pragma而不是progma.
reserved成員對我們的程式沒有什麼意義,它只是起到填補空間以達到位元組對齊的目的,當然即使不加這個成員通常編譯器也會給我們自動填補對齊,我們自己加上它只是起到顯式的提醒作用.
六.位元組對齊可能帶來的隱患:
p=&i;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;
最後兩句**,從奇數邊界去訪問unsignedshort型變數,顯然不符合對齊的規定。
在x86上,類似的操作只會影響效率,但是在mips或者sparc上,可能就是乙個error,因為它們要求必須位元組對齊.
七.如何查詢與位元組對齊方面的問題:
如果出現對齊或者賦值問題首先檢視
1. 編譯器的big little端設定
2. 看這種體系本身是否支援非對齊訪問
3. 如果支援看設定了對齊與否,如果沒有則看訪問時需要加某些特殊的修飾來標誌其特殊訪問操作
舉例:[cpp]view plain
copy
#include
main()
; struct
b ;
#pragma pack (2) /*指定按2位元組對齊*/
struct
c ;
#pragma pack () /*取消指定對齊,恢復預設對齊*/
#pragma pack (1) /*指定按1位元組對齊*/
struct
d ;
#pragma pack ()/*取消指定對齊,恢復預設對齊*/
ints1=
sizeof
(struct
a);
ints2=
sizeof
(struct
b);
ints3=
sizeof
(struct
c);
ints4=
sizeof
(struct
d);
printf("%d\n"
,s1);
printf("%d\n"
,s2);
printf("%d\n"
,s3);
printf("%d\n"
,s4);
}
輸出:8
12 8
7
修改**:
struct a ;
struct b ;
輸出:4 4
輸出都是4,說明之前的int影響對齊!
看圖就明白了
c語言位元組對齊
現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體位址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這就是對齊。基本資料型別自身對齊,也叫自然對齊。就是說...
C語言位元組對齊
一 概念 對齊跟資料在記憶體中的位置有關。如果乙個變數的記憶體位址正好位於它長度的整數倍,他就被稱做自然對齊。比如在32位cpu下,假設乙個整型變數的位址為0x00000004,那它就是自然對齊的。二 為什麼要位元組對齊 需要位元組對齊的根本原因在於cpu訪問資料的效率問題。假設上面整型變數的位址不...
C語言位元組對齊
一 概念 對齊跟資料在記憶體中的位置有關。如果乙個變數的記憶體位址正好位於它長度的整數倍,他就被稱做自然對齊。比如在32位cpu下,假設乙個整型變數的位址為0x00000004,那它就是自然對齊的。二 為什麼要位元組對齊 在c語言中,結構是一種復合資料型別,其構成元素既可以是基本資料型別 如int ...