1、
什麼是位元組對齊問題?
一般來說,計算機按照其字長方式來定址可以提高執行效率,比如32位(4位元組)的x86
結構下,如果每次訪問的變數其位址都是4的倍數,則每次對變數的訪問只需要一次匯流排
操作。因此,編譯器為了迎合cpu的這一特性,一般在編譯的時候都會對變數的儲存方式
進行對齊處理。當然x86結構在不滿足位元組對其的情況也可以執行,只是損失了效率而已
,而有些體系結構,如powerpc或risc處理器(如arm),如果訪問非對齊方式的變數,
則會導致error。乙個簡單的例子:
struct test1;
sizeof(struct test1)結果是8而不是5 ,這就是位元組對齊問題了。
編譯器在a和b之間將插入3個填充位元組以滿足位元組對齊。
2、位元組對齊的原則
"成員對齊有乙個重要的條件,即每個成員按自己的方式對齊.其對齊的規則是,每個成
員按其型別的對齊引數(通常是這個型別的大小)和指定對齊引數(或者說設定位元組對齊時
所允許的最大值.這裡預設是gcc為4位元組,vc6.0為8位元組)中較小的乙個對齊.並且結構的
長度必須為所用過的所有對齊引數的整數倍,不夠就補空位元組."(引用)對於結構體的對齊
方式,ansi c標準規定結構體型別的對齊要求不能比它所有欄位中要求最嚴格的那個寬
松,可以更嚴格。
各基本型別的對齊原則是:
char 與位元組邊界對齊
short(16 位)
與偶數位元組邊界對齊
int 和 long(32 位)
與 32 位邊界對齊
float 與 32 位邊界對齊
double 與 64 位邊界對齊
structures 任何成員的最大對齊要求
unions 第乙個成員的對齊要求
我們來分析一下上面的結構體test1,(編譯器為gcc)
(1)成員a本身的方式為1位元組,gcc預設的最大值為4 ,取兩者之中較小的乙個,所以
成員a的對齊方式為1位元組;
(2)成員b本身的對齊方式為4位元組,gcc預設的最大值為4 ,取兩者之中較小的乙個,
所以成員b的對齊方式為4位元組;
(3)結構體test1的對齊方式的最小值為:所有欄位中所用到的對齊方式中的最大值,
即a和b的對齊方式之中的最大值,即4位元組對齊,一般結構其就直接取這個值為對齊方式
了;(4)結構體的長度必須是1和4的公倍數,而根據(1)(2)(3)的對齊方式排列下來
,結構體已經占用了8個位元組了
,是4的倍數,因此結構體大小為8位元組。
因此,編譯器在分配記憶體的時候,首先把整個結構體的首位址存放在4位元組對齊的位置(
或者說能整除4的位址),成員a的位址與結構體相同
,成員b的位址必須4位元組對齊,因
此,a後面空出3個位元組,在偏移4的地方存入成員b。
(5)對於結構體長度的規定,可以這樣理解,假設定義了乙個結構體
struct test2
定義結構體變數struct test2 t2; t2的首位址只需要滿足4位元組對齊方式即可,假設為0
x0,成員c的位址也為0x0,占用0x0-0x7的記憶體空間 ,成員d的對齊方式為位元組,占用0x8即
可。感覺上好像整個結構體只需要占用9個位元組,實際上如果我們定義結構體陣列,str
uct test2 t2[10];t2[0]的首位址為4的倍數,假設為0x0,如果整個結構體大小為9位元組
,則t2[1]的首位址將是0xa,不滿足4位元組對齊,因此結構體大小必須是12,以保證定義
陣列時也滿足對齊原則。
(6)對於結構體內有複雜資料型別成員(如結構體)時,以此類推,如
struct test3
成員c的對齊方式是min(4,4)
成員d的對齊方式是min(1,4)
成員t1的對齊方式是min(4,4)
整個結構體的對齊方式是max( min(4,4), min(1,4), min(4,4)
)整個結構體的大小必須是min(4,4), min(1,4), min(4,4)的最接近的公倍數。3、
設計結構體
設計結構體時需要充分考慮位元組對齊的問題, tcp/ip各報頭結構就設計的非常巧妙。舉
個簡單的例子,如果需要設計乙個結構體,有3個成員,兩個 short,則應這樣設計:
struct test4;
這樣整個結構體將占用6位元組(注意不是8位元組)。而如果設計成這樣:
struct test5;
4、改變系統預設對齊方式的巨集
gcc的預設對齊方式是4,vc6.0的預設對齊方式是8,如果想改變這些值時,可以使用#p
ragma pack(n),如
struct test1;
則sizeof(struct test1)結果就是5了。因為b的對齊方式將在其本身的對齊方式(4位元組
)和允許的最大對齊方式(或者說預設對齊方式,這裡被#pragma pack(1)改為1了)之
中取小的那個,則b的對齊方式是1位元組,整個結構體的對齊方式也是1位元組。這樣,a和
b之間將不會有空餘位元組,結構體a的首位址也不需要4位元組對齊。
5、乙個問題
struct test6;
這個結構體大小在gcc下是24,在vc60下是8,位址及偏移都滿足前面分析的原則,但是
在gcc下把#pragma pack(8)加上以後,其大小還是24,b4的位址並不是8位元組對齊,不知
道為什麼了。感覺gcc不能把允許的最大對齊方式(或者說預設對齊方式)改為比4大的
,即使改了也不起作用,不知道能吧能這樣理解,而#pragma pack(2)、#pragma pack(
1)都結果符合預期。
6、計算偏移的巨集
#define offset(struct,member) (&((struct*)0)->member)
printf ("%d, %d \n", offset(struct test1, a), offset(struct test1, b) );
7、判斷大小端幾種方法,第1種和第4種不依賴平台
int a = 0x12345678
假設a的位址為0x0
則我們習慣的小端模式下:
0x0 0x12
0x1 0x34
0x2 0x56
0x3 0x78
而如果大端模式:
0x0 0x78
0x1 0x56
0x2 0x43
0x3 0x21
(1)int
main ()
u;u.l = 1;
exit (u.c[sizeof (long) - 1] == 1);
}(2)
#include
#include
intmain ()
(3)#include
#include
intmain ()
(4) int is_little_endian()
8、網路裡面的大小端問題
網路裡面所有傳輸的資料均是大端模式,而通常的x86一般都採用小端模式(其他的一些
體系結構則可以通過跳線設定)。但是有幾點稍微需要注意一下:
(1)大小端轉換巨集
htons htonl
ntohs ntohl
(2)大小端問題對單位元組資料是無意義的,如:
if (ntohs (p_eth->h_proto) == eth_p_ip) ;
…ret = sendto (broadcast_fd, send_buff, sizeof (int), 0, (struct sockaddr *)&
to_addr, sizeof (to_addr));
…則抓包可以發現內部資料部分為01 00 00 00,即仍舊是小端模式。
位元組對齊問題
現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體位址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這就是對齊。對齊的作用和原因 各個硬體平台對儲存空間的...
位元組對齊問題
位元組序問題關係到資料結構設計的是否合理,合理的資料結構設計可以節省記憶體空間,同時還能夠提高資料訪問效率,這在資源有限的其嵌入式系統中是非常重要的.我們可以先看看這兩個例子就大概了解了位元組對齊的問題了。在pc機上char是佔1個位元組,而int是4個位元組。include include voi...
位元組對齊問題
為了能使cpu對變數進行高效快速的訪問,變數的起始位址應該具有某些特性,即所謂的 對齊 例如對於4位元組的int型別變數,其起始位址應位於4位元組邊界上,即起始位址能夠被4整除。變數的對齊規則如下 32位系統 type alignment char 在位元組邊界上對齊 short 16 bit 在雙...