字節對齊
一、原則:
1.結構體內成員按自身按自身長度自對齊。
自身長度,如char=1,short=2,int=4,double=8,。所謂自對齊,指的是該成員的起始位置的內存地址必須是它自身長度的整數倍。如int只能以0,4,8這類的地址開始
2.結構體的總大小為結構體的有效對齊值的整數倍
結構體的有效對齊值的確定:
1)當未明確指定時,以結構體中最長的成員的長度為其有效值
2)當用#pragma pack(n)指定時,以n和結構體中最長的成員的長度中較小者為其值。
3)當用__attribute__ ((__packed__))指定長度時,強制按照此值為結構體的有效對齊值
二、例子
1。
struct AA{
char a;
int b;
char c;
}aa
結果,sizeof(aa)=12
何解?首先假設結構體內存起始地址為0,那么地址的分布如下
0 a
1
2
3
4 b
5 b
6 b
7 b
8 c
9
10
11
char的字對齊長度為1,所以可以在任何地址開始,但是,int自對齊長度為4,必須以4的倍數地址開始。所以,盡管1-3空著,但b也只能從4開始。再加上c后,整個結構體的總長度為9,結構體的有效對齊值為其中最大的成員即int的長度4,所以,結構體的大小向上擴展到12,即9-11的地址空著。
2.
struct AA{
char a;
char c;
int b;
}aa
sizeof(aa)=8,為什么呢
0 a
1 c
2
3
4 b
5 b
6 b
7 b
因為c為char類型,字對齊長度為1,所以可以有效的利用1-3間的空格。看見了吧,變量定義的位置的不同時有可能影響結構體的大小的哦!
3.
#pragma pack(2)
struct AA{
char a;
int b;
char c;
}aa
sizeof(aa)=10,
為什么呢?a到c只占9字節長度,因為結構體的有效對齊長度在pack指定的2和int的4中取
較小的值2。故取2的倍數10。
如果當pack指定為8呢?那就仍然按4來對齊,結果仍然是12。
4.
struct AA{
char a;
int b;
char c;
}__attribute__((__8__))aa
sizeof(aa)=16,)
為咩?其實a到c仍然只占9字節長度,但結構體以8對齊,故取8的倍數16.
如果其指定2,則結果為10
如果pragma pack和__attribute__
同時指定呢?以__attribute__ 的為準。
需要說明的是,不管pragma
pack和__attribute__如何指定,結構體內部成員的自對齊仍然按照其自身的對齊值。
大端小端
字節序(大小端)詳解從高低地址和高低位開始理解(轉)
一、字節序定義
字節序,顧名思義字節的順序,再多說兩句就是大于一個字節類型的數據在內存中的存放順序(一個字節的數據當然就無需談順序的問題了)。
其實大部分人在實際的開發中都很少會直接和字節序打交道。唯有在跨平臺以及網絡程序中字節序才是一個應該被考慮的問題。
在所有的介紹字節序的文章中都會提到字節序分為兩類:Big-Endian和Little-Endian。引用標準的Big-Endian和Little-Endian的定義如下:
a) Little-Endian就是低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。
b) Big-Endian就是高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。
c) 網絡字節序:4個字節的32 bit值以下面的次序傳輸:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。這種傳輸次序稱作大端字節序。由于 TCP/IP首部中所有的二進制整數在網絡中傳輸時都要求以這種次序,因此它又稱作網絡字節序。比如,以太網頭部中2字節的“以太網幀類型”,表示后面數據的類型。對于ARP請求或應答的以太網幀類型來說,在網絡傳輸時,發送的順序是0x08,0x06。在內存中的映象如下圖所示:
棧底 (高地址)
---------------
0x06 -- 低位
0x08 -- 高位
---------------
棧頂 (低地址)
該字段的值為0x0806。按照大端方式存放在內存中。
二、高/低地址與高低字節
首先我們要知道我們C程序映像中內存的空間布局情況:在《C專家編程》中或者《Unix環境高級編程》中有關于內存空間布局情況的說明,大致如下圖:
----------------------- 最高內存地址 0xffffffff
| 棧底
.
. 棧
.
棧頂
-----------------------
|
|
\|/
NULL (空洞)
/|\
|
|
-----------------------
堆
-----------------------
未初始化的數據
----------------(統稱數據段)
初始化的數據
-----------------------
正文段(代碼段)
----------------------- 最低內存地址 0x00000000
以上圖為例如果我們在棧上分配一個unsigned char buf[4],那么這個數組變量在棧上是如何布局的呢[注1]?看下圖:
棧底 (高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
棧頂 (低地址)
現在我們弄清了高低地址,接著來弄清高/低字節,如果我們有一個32位無符號整型0x12345678(呵呵,恰好是把上面的那4個字節buf看成一個整型),那么高位是什么,低位又是什么呢?其實很簡單。在十進制中我們都說靠左邊的是高位,靠右邊的是低位,在其他進制也是如此。就拿 0x12345678來說,從高位到低位的字節依次是0x12、0x34、0x56和0x78。
高低地址和高低字節都弄清了。我們再來回顧一下Big-Endian和Little-Endian的定義,并用圖示說明兩種字節序:
以unsigned int value = 0x12345678為例,分別看看在兩種字節序下其存儲情況,我們可以用unsigned char buf[4]來表示value:
Big-Endian: 低地址存放高位,如下圖:
棧底 (高地址)
---------------
buf[3] (0x78) -- 低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) -- 高位
---------------
棧頂 (低地址)
Little-Endian: 低地址存放低位,如下圖:
棧底 (高地址)
---------------
buf[3] (0x12) -- 高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) -- 低位
---------------
棧頂 (低地址)
在現有的平臺上Intel的X86采用的是Little-Endian,而像Sun的SPARC采用的就是Big-Endian。
三、例子
嵌入式系統開發者應該對Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU對操作數的存放方式是從低字節到高字節,而Big-endian模式對操作數的存放方式是從高字節到低字節。
例如,16bit寬的數0x1234在Little-endian模式CPU內存中的存放方式(假設從地址0x4000開始存放)為:
內存地址 存放內容
0x4001 0x12
0x4000 0x34
而在Big-endian模式CPU內存中的存放方式則為:
內存地址 存放內容
0x4001 0x34
0x4000 0x12
32bit寬的數0x12345678在Little-endian模式CPU內存中的存放方式(假設從地址0x4000開始存放)為:
內存地址 存放內容
0x4003 0x12
0x4002 0x34
0x4001 0x56
0x4000 0x78
而在Big-endian模式CPU內存中的存放方式則為:
內存地址 存放內容
0x4003 0x78
0x4002 0x56
0x4001 0x34
0x4000 0x12
整合字節對齊+大端小端+程序內存數據區域劃分
內存分布
------高地址------
-
棧
棧又稱堆棧, 是用戶存放程序臨時創建的局部變量,也就是說我們函數括弧“{}”中定義的變量(但不包括static聲明的變量,static意味著在數據段中存放變量)。除此以外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,并且待到調用結束后,函數的返回值也會被存放回棧中。由于棧的先進先出特點,所以棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的內存區。 -
堆
堆是用于存放進程運行中被動態分配的內存段,它的大小并不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減) -
BBS
BSS段(bss segment)通常是指用來存放程序中未初始化,或初始化為0的全局變量,靜態局部變量的一塊內存區域。BSS是英文Block Started by Symbol的簡稱。BSS段屬于靜態內存分配。 -
數據
數據段(data segment)通常是指用來存放程序中已初始化為非0的全局變量的一塊內存區域。數據段屬于靜態內存分配。 -
代碼段(code segment/text segment)
通常是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,并且內存區域通常屬于只讀, 某些架構也允許代碼段為可寫,即允許修改程序。在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等。
------低地址------