大小端對位元位的影響

2021-08-20 22:22:25 字數 3413 閱讀 1205

(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

typedefstruct_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 位域 位域是指資訊在儲存時,並不需要占用完整的位元組,只需要占用幾個二進位制位。位域就是把乙個位元組中的二進位制位...