大端模式與小端模式

大端模式與小端模式

  一、概念及詳解

  在各種體系的計算機中通常採用的字節存儲機制主要有兩種:big-endian和little-endian,即大端模式和小端模式。

  先回顧兩個關鍵詞,MSB和LSB:

  MSB:Most Significant Bit -------最高有效位

        LSB:Least Significant Bit -------最低有效位

  大端模式(big-edian)

  big-endian:MSB存放在最低端的地址上。

  舉例,雙字節數0x1234以big-endian的方式存在起始地址0x00002000中:

| data |<-- address

        | 0x12 |<-- 0x00002000

        | 0x34 |<-- 0x00002001

  在Big-Endian中,對於bit序列中的序號編排方式如下(以雙字節數0x8B8A爲例):

bit | 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15

        ------MSB----------------------------------LSB

        val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |

        +--------------------------------------------+

        = 0x8 B 8 A

  小端模式(little-endian)

  little-endian:LSB存放在最低端的地址上。

  舉例,雙字節數0x1234以little-endian的方式存在起始地址0x00002000中:

| data |<-- address

        | 0x34 |<-- 0x00002000

        | 0x12 |<-- 0x00002001

  在Little-Endian中,對於bit序列中的序號編排和Big-Endian剛好相反,其方式如下(以雙字節數0x8B8A爲例):

bit | 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0

        ------MSB-----------------------------------LSB

        val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |

        +---------------------------------------------+

        = 0x8 B 8 A

 

  二、數組在大端小端情況下的存儲:

  以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) --低位

        --------------

        低地址

 

  三、大端小端轉換方法:

  Big-Endian轉換成Little-Endian如下:

#define BigtoLittle16(A)                 ((((uint16)(A) & 0xff00) >> 8) | \

                                                          (((uint16)(A) & 0x00ff) << 8))

#define BigtoLittle32(A)                 ((((uint32)(A) & 0xff000000) >> 24) | \

                                                          (((uint32)(A) & 0x00ff0000) >> 8) | \

                                                          (((uint32)(A) & 0x0000ff00) << 8) | \

                                                          (((uint32)(A) & 0x000000ff) << 24))

 

  四、大端小端檢測方法:

  如何檢查處理器是big-endian還是little-endian?

  聯合體union的存放順序是所有成員都從低地址開始存放,利用該特性就可以輕鬆地獲得了CPU對內存採用Little-endian還是Big-endian模式讀寫。

int checkCPUendian()

{

union

{

unsigned int a;

unsigned char b;

}c;

c.a = 1;

return (c.b == 1);

}

/*return 1 : little-endian, return 0:big-endian*/

 

 

網絡字節順序

1、字節內的比特位不受這種順序的影響

比如一個字節1000 0000(或表示爲十六進制80H)不管是什麼順序其內存中的表示法都是這樣。

2、大於1個字節的數據類型纔有字節順序問題

比如Byte A,這個變量只有一個字節的長度,所以根據上一條沒有字節順序問題。所以字節順序是“字節之間的相對順序”的意思。

3、大於1個字節的數據類型的字節順序有兩種

比如short B,這是一個兩字節的數據類型,這時就有字節之間的相對順序問題了。

網絡字節順序是“所見即所得”的順序。而Intel類型的CPU的字節順序與此相反。

比如上面的short B=0102H(十六進制,每兩位表示一個字節的寬度)。所見到的是“0102”,按一般數學常識,數軸從左到右的方向增加,即內存地址從左到右增加的話,在內存中這個short B的字節順序是:

01 02

這就是網絡字節順序。所見到的順序和在內存中的順序是一致的!

而相反的字節順序就不同了,其在內存中的順序爲:02 01

假設通過抓包得到網絡數據的兩個字節流爲:01 02

如果這表示兩個Byte類型的變量,那麼自然不需要考慮字節順序的問題。

如果這表示一個short變量,那麼就需要考慮字節順序問題。根據網絡字節順序“所見即所得”的規則,這個變量的值就是:0102

假設本地主機是Intel類型的,那麼要表示這個變量,有點麻煩:

定義變量short X,

字節流地址爲:pt,按順序讀取內存是爲

x=*((short*)pt);

那麼X的內存順序當然是01 02

按非“所見即所得”的規則,這個內存順序和看到的一樣顯然是不對的,所以要把這兩個字節的位置調換。

調換的方法可以自己定義,但用已經有的API還是更爲方便。

 

網絡字節順序與主機字節順序

NBO與HBO網絡字節順序NBO(Network Byte Order):按從高到低的順序存儲,在網絡上使用統一的網絡字節順序,可以避免兼容性問題。主機字節順序(HBO,Host Byte Order):不同的機器HBO不相同,與CPU設計有關計算機數據存儲有兩種字節優先順序:高位字節優先和低位字節優先。Internet上數據以高位字節優先順序在網絡上傳輸,所以對於在內部是以低位字節優先方式存儲數據的機器,在Internet上傳輸數據時就需要進行轉換。 

 

htonl()

簡述:

   將主機的無符號長整形數轉換成網絡字節順序。

    #include <winsock.h>

    u_long PASCAL FAR htonl( u_long hostlong);

    hostlong:主機字節順序表達的32位數。

註釋:

   本函數將一個32位數從主機字節順序轉換成網絡字節順序。

返回值:

    htonl()返回一個網絡字節順序的值。

 

inet_ntoa()

簡述:

將網絡地址轉換成“.”點隔的字符串格式。

#include <winsock.h>

char FAR* PASCAL FAR inet_ntoa( struct in_addr in);

in:一個表示Internet主機地址的結構。

註釋:

本函數將一個用in參數所表示的Internet地址結構轉換成以“.”間隔的諸如“a.b.c.d”的字符串形式。請注意inet_ntoa()返回的字符串存放在WINDOWS套接口實現所分配的內存中。應用程序不應假設該內存是如何分配的。在同一個線程的下一個WINDOWS套接口調用前,數據將保證是有效。

返回值:

若無錯誤發生,inet_ntoa()返回一個字符指針。否則的話,返回NULL。其中的數據應在下一個WINDOWS套接口調用前複製出來。

 

網絡中傳輸的數據有的和本地字節存儲順序一致,而有的則截然不同,爲了數據的一致性,就要把本地的數據轉換成網絡上使用的格式,然後發送出去,接收的時候也是一樣的,經過轉換然後纔去使用這些數據,基本的庫函數中提供了這樣的可以進行字節轉換的函數,如和htons( ) htonl( ) ntohs( ) ntohl( ),這裏n表示network,h表示host,htons( ) htonl( )用於本地字節向網絡字節轉換的場合,s表示short,即對2字節操作,l表示long即對4字節操作。同樣ntohs( )ntohl( )用於網絡字節向本地格式轉換的場合。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章