(1) 我們知道,儲存數字時,對小端機而言,數字的低位,存在低位址,高位存在高位址。大端機正相反。
(2) 讀取的方式,也是一樣的。對於小端機,讀出的低位址位作為數字的低位。
(3) 此外big-endian/little-endian儲存順序,不僅僅針對位元組,還針對位元組內的位元位。對於小端機而言,位元組內的8個位元,低位址端位元位,對應二進位制數字的低位。
(4) 對於結構體的多個位域,和普通成員一樣,編譯器同樣按照位址由低到高順序儲存,無論是大端機還是小端機。只是位域內的位元順序有區別罷了。
(5) 表述乙個數值,可以使用兩種檢視 :
第乙個是「邏輯檢視」,通俗的表述方式,也就是我們平時在書本上看到的,手寫數字時的方式。左邊為高位,右邊為低位。例如 375,-4.1,036,0xaf4d215b
另乙個是「記憶體檢視」,即數字在記憶體中的儲存方式,是我們程式設計師專有的一種表述方式。左邊為低位址位元組,右邊為高位址位元組。位元組內左邊為低位址位元位,右邊為高位址位元位。很明顯,同乙個unsigned int值,在大端機、小端機上,分別有兩種不同的「記憶體檢視」。
例如,uint16 0x2a1f,二進位制位元位為0010 1010 0001 1111 (顯然這一行使用的就是「邏輯檢視」)
在小端機上的「記憶體檢視」為:1111 1000 0101 0100 (低位址 -> 高位址)
在大端機上的「記憶體檢視」為:0010 1010 0001 1111 (低位址 -> 高位址)
另外可以看到,大端機的"記憶體檢視"和"邏輯檢視"是相同的。在很多相關的文章裡,並沒有去區分數字的兩種表述方式,導致了很多混淆。其次,很多例子使用16進製制,只能用於表達位元組序,無法精確表達內部的位元順序。
再舉乙個上一節使用過的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef
struct
_exam_
exam;
exam ex;
ex.tag = 4;
ex.field1 = 2;
ex.field2 = 0x3a;
ex.field3 = 0x4c1;
ex.pad = 0;
變數ex的6個位域的"記憶體檢視",在大端機是000100 010 0111010 10010110001 00000(低位址->高位址),在小端機是001000 010 0101110 10001101001 00000(低位址->高位址)。可見位域順序是一樣的,但是位域內位元位順序不同。
若按照4位一組,大端機"記憶體檢視"為 0001 0001 0011 1010 1001 0110 0010 0000,如果按照unsigned int的方式讀取這塊記憶體,結果是0x113a9620,四個位元位對應乙個16進製制數,和"邏輯檢視"完全一樣
在小端機上4位一組排列,"記憶體檢視"為 0010 0001 0010 1110 1000 1101 0010 0000,如果按照unsigned int的方式讀取這塊記憶體,就會按照小端機的方式來解析記憶體。可以先把二進位制翻譯為"邏輯檢視" - 把整個"記憶體檢視"32位顛倒順序,結果是0x04b17484,注意不是0x212e8d20.
那麼這些規則,對位域值的讀取有什麼影響呢?
位元組流在網路上傳輸是按照網路位元組序傳輸的,也就是大端序。網絡卡不知道資料的含義(到底是int還是double,還是什麼image),只能看到乙個個位元組,因此它做的就是把每個位元組的8個位元位轉換為本機的位序。而具體的內容,則由我們的程式處理。比如對於整形等,呼叫socket介面的ntohl(),htonl()...等函式轉換位元組序。順便提一句,對於float/double型別,可以直接memcpy到乙個整形裡面,之後按照整形正常的處理流程,到了目標機後,再memcpy到乙個float/double裡。
char,short,int,long等2次冪大小的整形,作為乙個單獨的整體,經過整個流程梳理是沒有任何問題的。但無法保證結構體內的多個位域,按照定義的先後順序,從低位址到高位址排列。這意味著,無論如何,直接在**中使用ex.tag的方式,是讀不出tag位域的資料的。
細分有如下幾種情況:
(1) 主機內部傳輸無任何影響,畢竟是一樣的cpu架構。
(2) 相同位元組序的主機間傳輸,同樣沒有影響。因為經過二次socket+網絡卡轉換後,碼流是相同的。讀者可自行驗證。
(3) 大端機傳輸到小端機(上一節所描述的)。下列二進位制值如沒有特殊說明,都是"記憶體檢視"。
還以上面的位域為例,在大端機的為000100
0100111010
10010110001
00000(低位址->高位址),按照四位元一組為: 0001 00
01 0
011 1010
1001 0110 001
0 0000
傳輸到網路中,由於大端序和網路序相同,所以網絡卡不做轉換,位元組流按照先後,依然是 0001 00
01 0
011 1010
1001 0110 001
0 0000
傳輸到小端機,網絡卡自動轉換每個位元組的位元序,但位元組順序維持原狀, 10
00 1000
0101 110
00110 1001
0000 0
100,可見原先跨位元組相連的位域被"打散了"。位元組內的位域,雖然位元順序對了,但是從低位元位挪到了高位元位,位置錯了。
呼叫ntohl,位元序不變,轉換位元組序,0000 0
100 0110 1001
0101 110
0 10
00 1000,效果是跨位元組位域再次連通了。位域記憶體位址順序,正好和原先相反。如果把大端機的記憶體檢視畫到一張紙上,相當於翻到紙的背面。
此時,將這4個位元組碼流當作unsigned int,得到乙個"無符號整形",其"邏輯檢視"等於大端機上的「記憶體檢視」。左邊恰好是結構體最開始的位域:0001 00
01 0
011 1010
1001 0110 001
0 0000。因此我們將錯就錯,直接使用位操作符來左移相應的位數(需要計算後邊所有位域的總位元數),即可得到對應的位域值。位移操作符等,都是對"邏輯檢視"操作的。
(4) 小端機傳到大端機。網絡卡轉換+ntohl轉換後,依然在記憶體中得到乙個位域順序和正常順序相反的"無符號整形"。只是這次使用位運算子要注意,第乙個位域在"邏輯檢視"的最右邊,依次向左類推,和(3)的情形是相反的。
大小端對位元組序和位序的影響
1 位元組序 byte order 大端儲存格式 即多位元組資料的高位元組儲存在低位址中,而低位元組資料存放在高位址中。小端儲存格式 即多位元組資料的高位元組儲存在高位址中,而低位元組資料存放在低位址中。例子 short型別的資料 0x0201 大端儲存格式 存放內容 0x02 0x01 記憶體位址...
大小端 位域
小端 低位 lsb 位於低位址 大端 高位 msb 位於低位址 x86一般使用小端模式 位域 typedef union liteu32 t test t 位域的分配 小端 從lsb msb,從低位址到高位址 大端 從msb lsb,從高位址到低位址 例如 test t tt tt.t 0x3f p...
大小端,位域
大小端是指資料在記憶體中存放的順序,大於乙個位元組的整數,在記憶體中低位元組在前的就是小端,高位元組在前的就是大端。用c語言程式來判斷大端機還是小端機 include int main 位域 位域是指資訊在儲存時,並不需要占用完整的位元組,只需要占用幾個二進位制位。位域就是把乙個位元組中的二進位制位...