結構體位元組對齊
(1)什麼是位元組對齊
乙個變數占用 n 個位元組,則該變數的起始位址必須能夠被 n 整除,即: 存放起始位址 % n = 0, 對於結構體而言,這個 n 取其成員種的資料型別佔空間的值最大的那個。
(2)為什麼要位元組對齊
記憶體空間是按照位元組來劃分的,從理論上說對記憶體空間的訪問可以從任何位址開始,但是在實際上不同架構的cpu為了提高訪問記憶體的速度,就規定了對於某些型別的資料只能從特定的起始位置開始訪問。這樣就決定了各種資料型別只能按照相應的規則在記憶體空間中存放,而不能乙個接乙個的順序排列。
舉個例子,比如有些平台訪問記憶體位址都從偶數字址開始,對於乙個int型(假設32位系統),如果從偶數字址開始的地方存放,這樣乙個讀週期就可以讀出這個int資料,但是如果從奇數字址開始的位址存放,就需要兩個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到這個int資料,這樣明顯降低了讀取的效率。
(3)如何進行位元組對齊
每個成員按其型別的對齊引數(通常是這個型別的大小)和指定對齊引數(不指定則取預設值)中較小的乙個對齊,並且結構的長度必須為所用過的所有對齊引數的整數倍,不夠就補空位元組。
這個規則有點苦澀,可以把這個規則分解一下,前半句的意思先獲得對齊值後與指定對齊值進行比較,其中對齊值獲得方式如下:
1. 資料型別的自身對齊值為:對於char型資料,其自身對齊值為1,對於short型為2,對於int, long, float型別,其自身對齊值為4,對於 double 型別其自身對齊值為8,單位為位元組。
2.結構體自身對齊值:其成員中自身對齊值最大的那個值。
其中指定對齊值獲得方式如下:
#pragma pack (value)時的指定對齊值value。
未指定則取預設值。
後半句的意思是主要是針對於結構體的長度而言,因為針對資料型別的成員,它僅有乙個對齊引數,其本身的長度、於這個對齊引數,即1倍。對於結構體而言,它可能使用了多種資料型別,那麼這句話翻譯成對齊規則:每個成員的起始位址 % 自身對齊值 = 0,如果不等於 0 則先補空位元組直至這個表示式成立。
換句話說,對於結構體而言,結構體在在記憶體的存放順序用如下規則即可對映出來:
(一)每個成員的起始位址 % 每個成員的自身對齊值 = 0,如果不等於 0 則先補空位元組直至這個表示式成立;
(二)結構體的長度必須為結構體的自身對齊值的整數倍,不夠就補空位元組。
舉個例子:
#pragma pack(8)
struct a;
struct b;
struct c;
struct d;
struct e;
對於 struct a 來說,對於char型資料,其自身對齊值為1,對於long型別,其自身對齊值為4, 結構體的自身對齊值取其成員最大的對齊值,即大小4。那麼struct a 在記憶體中的順序步驟為:
(1) char a, 位址範圍為0x0000~0x0000,起始位址為0x0000,滿足 0x0000 % 1 = 0,這個成員位元組對齊了。
(2) long b, 位址起始位置不能從0x00001開始,因為 0x0001 % 4 != 0, 所以先補空位元組,直到0x00003結束,即補3個位元組的空位元組,從0x00004開始存放b,其位址範圍為0x00004~0x0007.
(3)此時成員都存放結束,結構體長度為8,為結構體自身對齊值的2倍,符合條件(二).
此時滿足條件(一)和條件(二),struct a 中各成員在記憶體中的位置為:a***b ,sizeof(struct a) = 8。(每個星號代表一位,成員各自代表自己所佔的位,比如a佔一位,b佔四位)
對於struct b,裡面有個型別為struct a的成員b自身對齊值為4,對於long型別,其自身對齊值為4. 故struct b的自身對齊值為4。那麼struct b 在記憶體中的順序步驟為:
(1) char a, 位址範圍為0x0000~0x0000,起始位址為0x0000,滿足 0x0000 % 1 = 0,這個成員位元組對齊了。
(2) struct a b, 位址起始位置不能從0x00001開始,因為 0x0001 % 4 != 0, 所以先補空位元組,直到0x00003結束,即補3個位元組的空位元組,從0x00004開始存放b,其位址範圍為0x00004~0x0000b.
(3) long c,位址起始位置從0x000012開始, 因為 0x0012 % 4 = 0,其位址範圍為0x0000c~0x00010.
(4)此時成員都存放結束,結構體長度為16,為結構體自身對齊值的4倍,符合條件(二).
此時滿足條件(一)和條件(二),struct b 中各成員在記憶體中的位置為:a***b c ,sizeof(struct b) = 16。(每個星號代表一位,成員各自代表自己所佔的位,比如a佔一位,b佔八位,c佔四位)
a*** b.a***b.c c
對於struct c,裡面有個型別為struct a的成員b自身對齊值為4,對於double 型別,其自身對齊值為8. 故struct c的自身對齊值為8。那麼struct c 在記憶體中的順序步驟為:
(1) char a, 位址範圍為0x0000~0x0000,起始位址為0x0000,滿足 0x0000 % 1 = 0,這個成員位元組對齊了。
(2) struct a b, 位址起始位置不能從0x00001開始,因為 0x0001 % 4 != 0, 所以先補空位元組,直到0x00003結束,即補3個位元組的空位元組,從0x00004開始存放b,其位址範圍為0x00004~0x0000b.
(3) double c,位址起始位置不能從0x000012開始, 因為 0x0012 % 8 != 0,所以先補空位元組,直到0x0000f結束,即補4個位元組的空位元組,從0x00010開始存放c,其位址範圍為0x00010~0x0018.
(4)此時成員都存放結束,結構體長度為24,為結構體自身對齊值的3倍,符合條件(二).
此時滿足條件(一)和條件(二),struct c 中各成員在記憶體中的位置為:a*** b **** c ,sizeof(struct c) = 24。(每個星號代表一位,成員各自代表自己所佔的位,比如a佔一位,b佔八位,c佔八位)
對於struct d,自身對齊值為8。前面三個成員與 struct c 是一致的。對於第四成員d,因為 0x0024 % 4 = 0, 所以可以從0x0024開始存放d, 其位址範圍為0x00024~0x00027.此時成員都存放結束,結構體長度為28,28 不是結構體自身對齊值8的倍數,所以要在後面補四個空格,即在0x0028~0x0031上補四個空格。補完了,結構體長度為32, 為結構體自
身對齊值的4被,,符合條件(二).
此時滿足條件(一)和條件(二),struct d 中各成員在記憶體中的位置為:a*** b **** c d **** ,sizeof(struct d) = 32。(每個星號代表一位,成員各自代表自己所佔的位,比如a佔一位,b佔八位,c佔八位, d佔四位)。
對於struct e 中各成員在記憶體中的位置為:a*** b c d, sizeof(struct e) = 24。(每個星號代表一位,成員各自代表自己所佔的位,比如a佔一位,b佔四位,c佔八位, d佔八位)。
通過struct d 和 struct e 可以看出,在成員數量和型別一致的情況,後者的所佔空間少於前者,因為後者的填充空位元組要少。如果我們在程式設計時考慮節約空間的話,應該遵循將變數按照型別大小從小到大宣告的原則, 這樣儘量減少填補空間。另外,可以在填充空位元組的地方來插入reserved成員, 例如
struct a
;這樣做的目的主要是為了對程式設計師起乙個提示作用,如果不加則編譯器會自動補齊。
習題typedef struct
int a;
//arm(int=4) 51(int=2)
char b;
// 1
short c;
// 2
}aaa;
typedef struct
char b;
int a;
short c;
}bbb;
i = sizeof(aaa);
j = sizeof(bbb);
//注意在51微控制器,arm,pc不同
51 (i=j=5)
//好像強制單位元組對齊
arm(i=8,j=12) //按規則預設對齊
pc(i=8,j=12)
aaa對齊方式如下(arm)
i i i i
i0 i i
bbb對齊方式如下(arm)
i 0 0 0
i i i i
i i 0 0
通過#pragma pack可以調整對齊位元組數
#pragma pack(1) //指定align為 1位元組;
。。。。。。。。。。。。//需要對齊的結構體
#pragma pack() //恢復到原先值
結構體位元組對齊
include pragma pack 2 struct t.pragma pack int main int argc,char argv 最後輸出的結果為 8。這個表示是按照2位元組來對齊資料,首先分配2位元組給成員變數i,分配完成後,還剩一位元組,zj add補0 沒法容納成員變數d,此時會再...
結構體位元組對齊
include pragma pack 2 struct t.pragma pack int main int argc,char argv 最後輸出的結果為 8。這個表示是按照2位元組來對齊資料,首先分配2位元組給成員變數i,分配完成後,還剩一位元組,zj add補0 沒法容納成員變數d,此時會再...
結構體位元組對齊
在用sizeof運算子求算某結構體所佔空間時,並不是簡單地將結構體中所有元素各自佔的空間相加,這裡涉及到記憶體位元組對齊的問題。從理論上講,對於任何 變數的訪問都可以從任何位址開始訪問,但是事實上不是如此,實際上訪問特定型別的變數只能在特定的位址訪問,這就需要各個變數在空間上按一定的規則排列,而不是...