亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1981 | 回復(fù): 0
打印 上一主題 下一主題

字節(jié)序問題--大端法小端法 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2011-12-22 08:51 |只看該作者 |倒序?yàn)g覽

一、字節(jié)序定義

字節(jié)序,顧名思義字節(jié)的順序,再多說兩句就是大于一個字節(jié)類型的數(shù)據(jù)在內(nèi)存中的存放順序(一個字節(jié)的數(shù)據(jù)當(dāng)然就無需談順序的問題了)。

其實(shí)大部分人在實(shí)際的開發(fā)中都很少會直接和字節(jié)序打交道。唯有在跨平臺以及網(wǎng)絡(luò)程序中字節(jié)序才是一個應(yīng)該被考慮的問題。

在所有的介紹字節(jié)序的文章中都會提到字節(jié)序分為兩類:Big-Endian和Little-Endian。引用標(biāo)準(zhǔn)的Big-Endian和Little-Endian的定義如下:
a) Little-Endian就是低位字節(jié)排放在內(nèi)存的低地址端,高位字節(jié)排放在內(nèi)存的高地址端。
b) Big-Endian就是高位字節(jié)排放在內(nèi)存的低地址端,低位字節(jié)排放在內(nèi)存的高地址端。
c) 網(wǎng)絡(luò)字節(jié)序:4個字節(jié)的32 bit值以下面的次序傳輸:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。這種傳輸次序稱作大端字節(jié)序。由于TCP/IP首部中所有的二進(jìn)制整數(shù)在網(wǎng)絡(luò)中傳輸時都要求以這種次序,因此它又稱作網(wǎng)絡(luò)字節(jié)序。比如,以太網(wǎng)頭部中2字節(jié)的“以太網(wǎng)幀類型”,表示后面數(shù)據(jù)的類型。對于ARP請求或應(yīng)答的以太網(wǎng)幀類型來說,在網(wǎng)絡(luò)傳輸時,發(fā)送的順序是0x08,0x06。在內(nèi)存中的映象如下圖所示:
棧底 (高地址)
---------------
0x06 -- 低位 
0x08 -- 高位
---------------
棧頂 (低地址)
該字段的值為0x0806。按照大端方式存放在內(nèi)存中。

二、高/低地址與高低字節(jié)

首先我們要知道我們C程序映像中內(nèi)存的空間布局情況:在《C專家編程》中或者《Unix環(huán)境高級編程》中有關(guān)于內(nèi)存空間布局情況的說明,大致如下圖:
----------------------- 最高內(nèi)存地址 0xffffffff
 | 棧底
 .
 .              棧
 .
  棧頂
-----------------------
 |
 |
\|/

NULL (空洞)

/|\
 |
 |
-----------------------
                堆
-----------------------
未初始化的數(shù)據(jù)
----------------(統(tǒng)稱數(shù)據(jù)段)
初始化的數(shù)據(jù)
-----------------------
正文段(代碼段)
----------------------- 最低內(nèi)存地址 0x00000000

以上圖為例如果我們在棧上分配一個unsigned char buf[4],那么這個數(shù)組變量在棧上是如何布局的呢[注1]?看下圖:
棧底 (高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
棧頂 (低地址)

現(xiàn)在我們弄清了高低地址,接著來弄清高/低字節(jié),如果我們有一個32位無符號整型0x12345678(呵呵,恰好是把上面的那4個字節(jié)buf看成一個整型),那么高位是什么,低位又是什么呢?其實(shí)很簡單。在十進(jìn)制中我們都說靠左邊的是高位,靠右邊的是低位,在其他進(jìn)制也是如此。就拿0x12345678來說,從高位到低位的字節(jié)依次是0x12、0x34、0x56和0x78。

高低地址和高低字節(jié)都弄清了。我們再來回顧一下Big-Endian和Little-Endian的定義,并用圖示說明兩種字節(jié)序:
以unsigned int value = 0x12345678為例,分別看看在兩種字節(jié)序下其存儲情況,我們可以用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) -- 低位
---------------
棧頂 (低地址)

在現(xiàn)有的平臺上Intel的X86采用的是Little-Endian,而像Sun的SPARC采用的就是Big-Endian。

三、例子

嵌入式系統(tǒng)開發(fā)者應(yīng)該對Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU對操作數(shù)的存放方式是從低字節(jié)到高字節(jié),而Big-endian模式對操作數(shù)的存放方式是從高字節(jié)到低字節(jié)。

例如,16bit寬的數(shù)0x1234在Little-endian模式CPU內(nèi)存中的存放方式(假設(shè)從地址0x4000開始存放)為:

內(nèi)存地址  存放內(nèi)容
 0x4001    0x12
 0x4000    0x34

而在Big-endian模式CPU內(nèi)存中的存放方式則為:

內(nèi)存地址  存放內(nèi)容
 0x4001    0x34
 0x4000    0x12
 
32bit寬的數(shù)0x12345678在Little-endian模式CPU內(nèi)存中的存放方式(假設(shè)從地址0x4000開始存放)為:

內(nèi)存地址  存放內(nèi)容
 0x4003     0x12
 0x4002     0x34
 0x4001     0x56
 0x4000     0x78
 
而在Big-endian模式CPU內(nèi)存中的存放方式則為:

內(nèi)存地址  存放內(nèi)容
 0x4003     0x78
 0x4002     0x56
 0x4001     0x34
 0x4000     0x12

 

 

 

 

大端法、小端法、網(wǎng)絡(luò)字節(jié)序 轉(zhuǎn)

關(guān)于字節(jié)序(大端法、小端法)的定義

《UNXI網(wǎng)絡(luò)編程》定義:術(shù)語“小端”和“大端”表示多字節(jié)值的哪一端(小端或大端)存儲在該值的起始地址。小端存在起始地址,即是小端字節(jié)序;大端存在起始地址,即是大端字節(jié)序。

 

也可以說:
1.小端法(Little-Endian)就是低位字節(jié)排放在內(nèi)存的低地址端即該值的起始地址,高位字節(jié)排放在內(nèi)存的高地址端。
2.大端法(Big-Endian)就是高位字節(jié)排放在內(nèi)存的低地址端即該值的起始地址,低位字節(jié)排放在內(nèi)存的高地址端。

舉個簡單的例子,對于整形0x12345678。它在大端法和小端法的系統(tǒng)內(nèi)中,分別如圖1所示的方式存放。

 

 

網(wǎng)絡(luò)字節(jié)序

我們知道網(wǎng)絡(luò)上的數(shù)據(jù)流是字節(jié)流,對于一個多字節(jié)數(shù)值,在進(jìn)行網(wǎng)絡(luò)傳輸?shù)臅r候,先傳遞哪個字節(jié)?也就是說,當(dāng)接收端收到第一個字節(jié)的時候,它是將這個字節(jié)作為高位還是低位來處理呢?
網(wǎng)絡(luò)字節(jié)序定義:收到的第一個字節(jié)被當(dāng)作高位看待,這就要求發(fā)送端發(fā)送的第一個字節(jié)應(yīng)當(dāng)是高位。而在發(fā)送端發(fā)送數(shù)據(jù)時,發(fā)送的第一個字節(jié)是該數(shù)字在內(nèi)存中起始地址對應(yīng)的字節(jié)?梢姸嘧止(jié)數(shù)值在發(fā)送前,在內(nèi)存中數(shù)值應(yīng)該以大端法存放。
網(wǎng)絡(luò)字節(jié)序說是大端字節(jié)序。
比如我們經(jīng)過網(wǎng)絡(luò)發(fā)送0x12345678這個整形,在80X86平臺中,它是以小端法存放的,在發(fā)送前需要使用系統(tǒng)提供的htonl將其轉(zhuǎn)換成大端法存放,如圖2所示。

 

字節(jié)序測試程序

不同cpu平臺上字節(jié)序通常也不一樣,下面寫個簡單的C程序,它可以測試不同平臺上的字節(jié)序。

#include <stdio.h>

#include <netinet/in.h>

int main()

{

    int i_num = 0x12345678;

    printf("[0]:0x%x\n", *((char *)&i_num + 0));

    printf("[1]:0x%x\n", *((char *)&i_num + 1));

    printf("[2]:0x%x\n", *((char *)&i_num + 2));

    printf("[3]:0x%x\n", *((char *)&i_num + 3));

10 

 

11 

    i_num = htonl(i_num);

12 

    printf("[0]:0x%x\n", *((char *)&i_num + 0));

13 

    printf("[1]:0x%x\n", *((char *)&i_num + 1));

14 

    printf("[2]:0x%x\n", *((char *)&i_num + 2));

15 

    printf("[3]:0x%x\n", *((char *)&i_num + 3));

16 

 

17 

    return 0;

18 


在80X86CPU平臺上,執(zhí)行該程序得到如下結(jié)果:
[0]:0x78
[1]:0x56
[2]:0x34
[3]:0x12

[0]:0x12
[1]:0x34
[2]:0x56
[3]:0x78

分析結(jié)果,在80X86平臺上,系統(tǒng)將多字節(jié)中的低位存儲在變量起始地址,使用小端法。htonl將i_num轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序,可見網(wǎng)絡(luò)字節(jié)序是大端法。

 

 

 

大端(Big Endian)與小端(Little Endian)簡介

///////////////////////////////////////////////////////

1. 你從哪里來?

端模式(Endian)的這個詞出自Jonathan Swift書寫的《格列佛游記》。這本書根據(jù)將雞蛋敲開的方法不同將所有的人分為兩類,從圓頭開始將雞蛋敲開的人被歸為Big Endian,從尖頭開始將雞蛋敲開的人被歸為Littile Endian。小人國的內(nèi)戰(zhàn)就源于吃雞蛋時是究竟從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開。在計(jì)算機(jī)業(yè)Big Endian和Little Endian也幾乎引起一場戰(zhàn)爭。在計(jì)算機(jī)業(yè)界,Endian表示數(shù)據(jù)在存儲器中的存放順序。采用大端方式進(jìn)行數(shù)據(jù)存放符合人類的正常思維,而采用小端方式進(jìn)行數(shù)據(jù)存放利于計(jì)算機(jī)處理。下文舉例說明在計(jì)算機(jī)中大小端模式的區(qū)別。

//////////////////////////////////////////////////////

2. 讀書百遍其義自見

小端口訣: 高高低低 -> 高字節(jié)在高地址, 低字節(jié)在低地址

大端口訣: 高低低高 -> 高字節(jié)在低地址, 低字節(jié)在高地址

 

long test = 0x313233334;

小端機(jī)器:

低地址 --> 高地址

00000010: 34 33 32 31         -> 4321

 

大端機(jī)器:

低地址 --> 高地址

00000010: 31 32 33 34         -> 4321

test變量存儲的是的0x10這個地址,

那編譯器怎么知道是讀四個字節(jié)呢? -> 根據(jù)變量test的類型long可知這個變量占據(jù)4個字節(jié).

那編譯器怎么讀出這個變量test所代表的值呢? -> 這就根據(jù)是little endian還是big endian來讀取

所以, 小端, 其值為0x31323334; 大端, 其值為0x34333231

 

htonl(test) 的情況:     ->其值為: 0x34333231

小端機(jī)器:

00000010: 31 32 33 34         -> 1234

大端機(jī)器:

00000010: 34 33 32 31         -> 4321

/////////////////////////////////////////////////////////////////////////////////////

3. 拿來主義

Byte Endian是指字節(jié)在內(nèi)存中的組織,所以也稱它為Byte Ordering,或Byte Order。

     對于數(shù)據(jù)中跨越多個字節(jié)的對象, 我們必須為它建立這樣的約定:

(1) 它的地址是多少?

(2) 它的字節(jié)在內(nèi)存中是如何組織的?

    針對第一個問題,有這樣的解釋:

    對于跨越多個字節(jié)的對象,一般它所占的字節(jié)都是連續(xù)的,它的地址等于它所占字節(jié)最低地址。(鏈表可能是個例外, 但鏈表的地址可看作鏈表頭的地址)。

    比如: int x,它的地址為0x100。 那么它占據(jù)了內(nèi)存中的Ox100, 0x101, 0x102, 0x103這四個字節(jié)(32位系統(tǒng),所以int占用4個字節(jié))。

    上面只是內(nèi)存字節(jié)組織的一種情況: 多字節(jié)對象在內(nèi)存中的組織有一般有兩種約定。 考慮一個W位的整數(shù)。

    它的各位表達(dá)如下:[Xw-1, Xw-2, ... , X1, X0],它的

    MSB (Most Significant Byte, 最高有效字節(jié))為 [Xw-1, Xw-2, ... Xw-8];

    LSB (Least Significant Byte, 最低有效字節(jié))為 [X7,X6,..., X0]。

    其余的字節(jié)位于MSB, LSB之間。

 

LSB和MSB誰位于內(nèi)存的最低地址,即誰代表該對象的地址?

這就引出了大端(Big Endian)與小端(Little Endian)的問題。

如果LSB在MSB前面,既LSB是低地址, 則該機(jī)器是小端; 反之則是大端。

DEC (Digital Equipment Corporation,現(xiàn)在是Compaq公司的一部分)和Intel的機(jī)器(X86平臺)一般采用小端。

IBM, Motorola(Power PC), Sun的機(jī)器一般采用大端。

當(dāng)然,這不代表所有情況。有的CPU即能工作于小端, 又能工作于大端,比如ARM, Alpha,摩托羅拉的PowerPC。 具體情形參考處理器手冊。

具體這類CPU是大端還是小端,應(yīng)該和具體設(shè)置有關(guān)。

(如,Power PC支持little-endian字節(jié)序,但在默認(rèn)配置時是big-endian字節(jié)序)

一般來說,大部分用戶的操作系統(tǒng)(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。

所以說,Little Endian還是Big Endian與操作系統(tǒng)和芯片類型都有關(guān)系。因此在一個處理器系統(tǒng)中,有可能存在大端和小端模式同時存在的現(xiàn)象。這一現(xiàn)象為系統(tǒng)的軟硬件設(shè)計(jì)帶來了不小的麻煩,這要求系統(tǒng)設(shè)計(jì)工程師,必須深入理解大端和小端模式的差別。大端與小端模式的差別體現(xiàn)在一個處理器的寄存器,指令集,系統(tǒng)總線等各個層次中。

 

Linux系統(tǒng)中,你可以在/usr/include/中(包括子目錄)查找字符串BYTE_ORDER(或

_BYTE_ORDER, __BYTE_ORDER),確定其值。BYTE_ORDER中文稱為字節(jié)序。這個值一般在endian.h或machine/endian.h文件中可以找到,有時在feature.h中,不同的操作系統(tǒng)可能有所不同。

 

【用函數(shù)判斷系統(tǒng)是Big Endian還是Little Endian】

enum {FALSE = 0, TRUE = !FALSE};

typedef short BOOL;

BOOL IsBig_Endian()

//如果字節(jié)序?yàn)閎ig-endian,返回true;

//反之為   little-endian,返回false

{

    unsigned short test = 0x1122;

    if(*( (unsigned char*) &test ) == 0x11)

       return TRUE;

else

    return FALSE;

 

}//IsBig_Endian()

 

//////////////////////////////////////////////////////////////////////////////

 

可以做個實(shí)驗(yàn)

在windows上下如下程序

#include <stdio.h>

#include <assert.h>

 

void main( void )

{

        short test;

        FILE* fp;

        

        test = 0x3132; //(31ASIIC碼的’1’,32ASIIC碼的’2’)

        if ((fp = fopen ("c:\\test.txt", "wb")) == NULL)

              assert(0);

        fwrite(&test, sizeof(short), 1, fp);

        fclose(fp);

}

    然后在C盤下打開test.txt文件,可以看見內(nèi)容是21,而test等于0x3132,可以明顯的看出來x86的字節(jié)順序是低位在前.如果我們把這段同樣的代碼放到(big-endian)的機(jī)器上執(zhí)行,那么打出來的文件就是12.這在本機(jī)中使用是沒有問題的.但當(dāng)你把這個文件從一個big- endian機(jī)器復(fù)制到一個little-endian機(jī)器上時就出現(xiàn)問題了.

    如上述例子,我們在big-endian的機(jī)器上創(chuàng)建了這個test文件,把其復(fù)制到little-endian的機(jī)器上再用fread讀到一個 short里面,我們得到的就不再是0x3132而是0x3231了,這樣讀到的數(shù)據(jù)就是錯誤的,所以在兩個字節(jié)順序不一樣的機(jī)器上傳輸數(shù)據(jù)時需要特別小心字節(jié)順序,理解了字節(jié)順序在可以幫助我們寫出移植行更高的代碼.

正因?yàn)橛凶止?jié)順序的差別,所以在網(wǎng)絡(luò)傳輸?shù)臅r候定義了所有字節(jié)順序相關(guān)的數(shù)據(jù)都使用big-endian,BSD的代碼中定義了四個宏來處理:

#define ntohs(n)     //網(wǎng)絡(luò)字節(jié)順序到主機(jī)字節(jié)順序 n代表net, h代表host, s代表short

#define htons(n)     //主機(jī)字節(jié)順序到網(wǎng)絡(luò)字節(jié)順序 n代表net, h代表host, s代表short

#define ntohl(n)      //網(wǎng)絡(luò)字節(jié)順序到主機(jī)字節(jié)順序 n代表net, h代表host, s代表 long

#define htonl(n)      //主機(jī)字節(jié)順序到網(wǎng)絡(luò)字節(jié)順序 n代表net, h代表host, s代表 long

 

舉例說明下這其中一個宏的實(shí)現(xiàn):

#define sw16(x) \

    ((short)( \

        (((short)(x) & (short)0x00ffU) << 8) | \

        (((short)(x) & (short)0xff00U) >> 8) ))

這里實(shí)現(xiàn)的是一個交換兩個字節(jié)順序.其他幾個宏類似.

 

我們改寫一下上面的程序

#include <stdio.h>

#include <assert.h>

 

#define sw16(x) \

    ((short)( \

        (((short)(x) & (short)0x00ffU) << 8) | \

        (((short)(x) & (short)0xff00U) >> 8) ))

 

#define sw32(x) \

((long)( \

   (((long)(x) & (long)0x000000ff) << 24) | \

   (((long)(x) & (long)0x0000ff00) << 8) | \

   (((long)(x) & (long)0x00ff0000) >> 8) | \

   (((long)(x) & (long)0xff000000) >> 24) ))

 

// 因?yàn)閤86下面是低位在前,需要交換一下變成網(wǎng)絡(luò)字節(jié)順序

#define htons(x) sw16(x)

#define htonl(x) sw32(x)

 

void main( void )

{

        short test;

        FILE* fp;

        

        test = htons(0x3132); //(31ASIIC碼的’1’,32ASIIC碼的’2’)

        if ((fp = fopen ("c:\\test.txt", "wb")) == NULL)

              assert(0);

        fwrite(&test, sizeof(short), 1, fp);

        fclose(fp);

}

 

    如果在高字節(jié)在前的機(jī)器上,由于與網(wǎng)絡(luò)字節(jié)順序一致,所以我們什么都不干就可以了,只需要把#define htons(x) sw16(x)宏替換為 #define htons(x) (x).

    一開始我在理解這個問題時,總在想為什么其他數(shù)據(jù)不用交換字節(jié)順序?比如說我們write一塊buffer到文件,最后終于想明白了,因?yàn)槎际?unsigned char類型一個字節(jié)一個字節(jié)的寫進(jìn)去,這個順序是固定的,不存在字節(jié)順序的問題

 

【大端(Big Endian)與小端(Little Endian)簡介】

Byte Endian是指字節(jié)在內(nèi)存中的組織,所以也稱它為Byte Ordering,或Byte Order。

     對于數(shù)據(jù)中跨越多個字節(jié)的對象, 我們必須為它建立這樣的約定:

(1) 它的地址是多少?

(2) 它的字節(jié)在內(nèi)存中是如何組織的?

    針對第一個問題,有這樣的解釋:

    對于跨越多個字節(jié)的對象,一般它所占的字節(jié)都是連續(xù)的,它的地址等于它所占字節(jié)最低地址。(鏈表可能是個例外, 但鏈表的地址可看作鏈表頭的地址)。

    比如: int x,它的地址為0x100。 那么它占據(jù)了內(nèi)存中的Ox100, 0x101, 0x102, 0x103這四個字節(jié)(32位系統(tǒng),所以int占用4個字節(jié))。

    上面只是內(nèi)存字節(jié)組織的一種情況: 多字節(jié)對象在內(nèi)存中的組織有一般有兩種約定。 考慮一個W位的整數(shù)。

    它的各位表達(dá)如下:[Xw-1, Xw-2, ... , X1, X0],它的

    MSB (Most Significant Byte, 最高有效字節(jié))為 [Xw-1, Xw-2, ... Xw-8];

    LSB (Least Significant Byte, 最低有效字節(jié))為 [X7,X6,..., X0]。

    其余的字節(jié)位于MSB, LSB之間。

 

LSB和MSB誰位于內(nèi)存的最低地址,即誰代表該對象的地址?

這就引出了大端(Big Endian)與小端(Little Endian)的問題。

如果LSB在MSB前面,既LSB是低地址, 則該機(jī)器是小端; 反之則是大端。

DEC (Digital Equipment Corporation,現(xiàn)在是Compaq公司的一部分)和Intel的機(jī)器(X86平臺)一般采用小端。

IBM, Motorola(Power PC), Sun的機(jī)器一般采用大端。

當(dāng)然,這不代表所有情況。有的CPU即能工作于小端, 又能工作于大端,比如ARM, Alpha,摩托羅拉的PowerPC。 具體情形參考處理器手冊。

具體這類CPU是大端還是小端,應(yīng)該和具體設(shè)置有關(guān)。

(如,Power PC支持little-endian字節(jié)序,但在默認(rèn)配置時是big-endian字節(jié)序)

一般來說,大部分用戶的操作系統(tǒng)(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。

所以說,Little Endian還是Big Endian與操作系統(tǒng)和芯片類型都有關(guān)系。

 

Linux系統(tǒng)中,你可以在/usr/include/中(包括子目錄)查找字符串BYTE_ORDER(或

_BYTE_ORDER, __BYTE_ORDER),確定其值。BYTE_ORDER中文稱為字節(jié)序。這個值一般在endian.h或machine/endian.h文件中可以找到,有時在feature.h中,不同的操作系統(tǒng)可能有所不同。

 

          big endian是指低地址存放最高有效字節(jié)(MSB),而little endian則是低地址存放最低有效字節(jié)(LSB)。

         用文字說明可能比較抽象,下面用圖像加以說明。比如數(shù)字0x12345678在兩種不同字節(jié)序CPU中的存儲順序如下所示:

 

Big Endian

 

   低地址                                            高地址

   ----------------------------------------->

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

   |     12     |      34    |     56      |     78    |

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

 

Little Endian

 

   低地址                                            高地址

   ----------------------------------------->

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

   |     78     |      56    |     34      |     12    |

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

 

        從上面兩圖可以看出,采用big endian方式存儲數(shù)據(jù)是符合我們?nèi)祟惖乃季S習(xí)慣的.

        為什么要注意字節(jié)序的問題呢?你可能這么問。當(dāng)然,如果你寫的程序只在單機(jī)環(huán)境下面運(yùn)行,并且不和別人的程序打交道,那么你完全可以忽略字節(jié)序的存在。但是,如果你的程序要跟別人的程序產(chǎn)生交互呢?在這里我想說說兩種語言。C/C++語言編寫的程序里數(shù)據(jù)存儲順序是跟編譯平臺所在的CPU相關(guān)的,而 J***A編寫的程序則唯一采用big endian方式來存儲數(shù)據(jù)。試想,如果你用C/C++語言在x86平臺下編寫的程序跟別人的J***A程序互通時會產(chǎn)生什么結(jié)果?就拿上面的 0x12345678來說,你的程序傳遞給別人的一個數(shù)據(jù),將指向0x12345678的指針傳給了J***A程序,由于J***A采取big endian方式存儲數(shù)據(jù),很自然的它會將你的數(shù)據(jù)翻譯為0x78563412。什么?竟然變成另外一個數(shù)字了?是的,就是這種后果。因此,在你的C程序傳給J***A程序之前有必要進(jìn)行字節(jié)序的轉(zhuǎn)換工作。

     無獨(dú)有偶,所有網(wǎng)絡(luò)協(xié)議也都是采用big endian的方式來傳輸數(shù)據(jù)的。所以有時我們也會把big endian方式稱之為網(wǎng)絡(luò)字節(jié)序。當(dāng)兩臺采用不同字節(jié)序的主機(jī)通信時,在發(fā)送數(shù)據(jù)之前都必須經(jīng)過字節(jié)序的轉(zhuǎn)換成為網(wǎng)絡(luò)字節(jié)序后再進(jìn)行傳輸。ANSI C中提供了下面四個轉(zhuǎn)換字節(jié)序的宏。

·BE和LE一文的補(bǔ)完

        我在8月9號的《Big Endian和Little Endian》一文中談了字節(jié)序的問題,原文見上面的超級鏈接?墒怯信笥讶匀粫䥺枺珻PU存儲一個字節(jié)的數(shù)據(jù)時其字節(jié)內(nèi)的8個比特之間的順序是否也有 big endian和little endian之分?或者說是否有比特序的不同?

     實(shí)際上,這個比特序是同樣存在的。下面以數(shù)字0xB4(10110100)用圖加以說明。

 

Big Endian

 

   msb                                                         lsb

   ---------------------------------------------->

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

   |   1 |   0 |   1 |   1 |   0 |   1 |   0 |   0 |

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

 

Little Endian

 

   lsb                                                         msb

   ---------------------------------------------->

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

   |   0 |   0 |   1 |   0 |   1 |   1 |   0 |   1 |

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

 

 

 

     實(shí)際上,由于CPU存儲數(shù)據(jù)操作的最小單位是一個字節(jié),其內(nèi)部的比特序是什么樣對我們的程序來說是一個黑盒子。也就是說,你給我一個指向0xB4這個數(shù)的指針,對于big endian方式的CPU來說,它是從左往右依次讀取這個數(shù)的8個比特;而對于little endian方式的CPU來說,則正好相反,是從右往左依次讀取這個數(shù)的8個比特。而我們的程序通過這個指針訪問后得到的數(shù)就是0xB4,字節(jié)內(nèi)部的比特序?qū)τ诔绦騺碚f是不可見的,其實(shí)這點(diǎn)對于單機(jī)上的字節(jié)序來說也是一樣的。

     那可能有人又會問,如果是網(wǎng)絡(luò)傳輸呢?會不會出問題?是不是也要通過什么函數(shù)轉(zhuǎn)換一下比特序?嗯,這個問題提得很好。假設(shè)little endian方式的CPU要傳給big endian方式CPU一個字節(jié)的話,其本身在傳輸之前會在本地就讀出這個8比特的數(shù),然后再按照網(wǎng)絡(luò)字節(jié)序的順序來傳輸這8個比特,這樣的話到了接收端不會出現(xiàn)任何問題。而假如要傳輸一個32比特的數(shù)的話,由于這個數(shù)在littel endian方存儲時占了4個字節(jié),而網(wǎng)絡(luò)傳輸是以字節(jié)為單位進(jìn)行的,little endian方的CPU讀出第一個字節(jié)后發(fā)送,實(shí)際上這個字節(jié)是原數(shù)的LSB,到了接收方反倒成了MSB從而發(fā)生混亂。

 

【用函數(shù)判斷系統(tǒng)是Big Endian還是Little Endian】

bool IsBig_Endian()

//如果字節(jié)序?yàn)閎ig-endian,返回true;

//反之為   little-endian,返回false

{

    unsigned short test = 0x1122;

    if(*( (unsigned char*) &test ) == 0x11)

       return TRUE;

else

    return FALSE;

 

}//IsBig_Endian()

 

三、例子

 

嵌入式系統(tǒng)開發(fā)者應(yīng)該對Little-endian和Big-endian模式非常了解。采用Little- endian模式的CPU對操作數(shù)的存放方式是從低字節(jié)到高字節(jié),而Big-endian模式對操作數(shù)的存放方式是從高字節(jié)到低字節(jié)。

 

例如,16bit寬的數(shù)0x1234在Little-endian模式CPU內(nèi)存中的存放方式(假設(shè)從地址 0x4000開始存放)為:

 

內(nèi)存地址 存放內(nèi)容

0x4001    0x12

0x4000    0x34

 

而在Big-endian模式CPU內(nèi)存中的存放方式則為:

 

內(nèi)存地址 存放內(nèi)容

0x4001    0x34

0x4000    0x12

 

32bit寬的數(shù)0x12345678在Little-endian模式CPU內(nèi)存中的存放方式(假設(shè)從地址0x4000開始存放)為:

 

內(nèi)存地址 存放內(nèi)容

0x4003     0x12

0x4002     0x34

0x4001     0x56

0x4000     0x78

 

而在Big-endian模式CPU內(nèi)存中的存放方式則為:

 

內(nèi)存地址 存放內(nèi)容

0x4003     0x78

0x4002     0x56

0x4001     0x34

0x4000     0x12

 

三、例子

測試平臺 : Sun SPARC Solaris 9 和 Intel X86 Solaris 9

我們的例子是這樣的:在使用不同字節(jié)序的平臺上使用相同的程序讀取同一個二進(jìn)制文件的內(nèi)容。

生成二進(jìn)制文件的程序如下 :

 

int main() {

        FILE    *fp = NULL;

        int     value = 0x12345678;

        int     rv = 0;

 

        fp = fopen("temp.dat", "wb");

        if (fp == NULL) {

                printf("fopen error\n");

                return -1;

        }

 

        rv = fwrite(&value, sizeof(value), 1, fp);

        if (rv != 1) {

                printf("fwrite error\n");

                return -1;

        }

 

        fclose(fp);

        return 0;

}

 

讀取二進(jìn)制文件的程序如下:

int main() {

        int             value   = 0;

        FILE         *fp     = NULL;

        int             rv      = 0;

        unsigned        char buf[4];

 

        fp = fopen("temp.dat", "rb");

        if (fp == NULL) {

                printf("fopen error\n");

                return -1;

        }

 

        rv = fread(buf, sizeof(unsigned char), 4, fp);

        if (rv != 4) {

                printf("fread error\n");

                return -1;

        }

 

        memcpy(&value, buf, 4); // or value = *((int*)buf);

        printf("the value is %x\n", value);

 

        fclose(fp);

        return 0;

}

 

測試過程:

(1) 在 SPARC 平臺下生成 temp.dat 文件

在 SPARC 平臺下讀取 temp.dat 文件的結(jié)果:

the value is 12345678

 

在 X86 平臺下讀取 temp.dat 文件的結(jié)果:

the value is 78563412

 

(1) 在 X86 平臺下生成 temp.dat 文件

在 SPARC 平臺下讀取 temp.dat 文件的結(jié)果:

the value is 78563412

 

在 X86 平臺下讀取 temp.dat 文件的結(jié)果:

the value is 12345678

 

[ 注 1]

buf[4] 在棧的布局我也是通過例子程序得到的:

int main() {

        unsigned char buf[4];

 

        printf("the buf[0] addr is %x\n", buf);

        printf("the buf[1] addr is %x\n", &buf[1]);

 

        return 0;

}

output:

SPARC 平臺:

the buf[0] addr is ffbff788

the buf[1] addr is ffbff789

X86 平臺:

the buf[0] addr is 8047ae4

the buf[1] addr is 8047ae5

 

兩個平臺都是 buf[x] 所在地址高于 buf[y] (x > y) 。

 

 

如何判斷系統(tǒng)是Big Endian還是Little Endian?

在/usr /include/中(包括子目錄)查找字符串BYTE_ORDER(或_BYTE_ORDER, __BYTE_ORDER),確定其值。這個值一般在endian.h或machine/endian.h文件中可以找到,有時在feature.h中,不同的操作系統(tǒng)可能有所不同。一般來說,Little Endian系統(tǒng)BYTE_ORDER(或_BYTE_ORDER,__BYTE_ORDER)為1234,Big Endian系統(tǒng)為4321。大部分用戶的操作系統(tǒng)(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。本質(zhì)上說,Little Endian還是Big Endian與操作系統(tǒng)和芯片類型都有關(guān)系。

 

Processor OS Order

x86 (Intel, AMD, … ) All little-endian

DEC Alpha All little-endian

HP-PA NT little-endian

HP-PA UNIX big-endian

SUN SPARC All? big-endian

MIPS NT little-endian

MIPS UNIX big-endian

PowerPC NT little-endian

PowerPC non-NT big-endian

RS/6000 UNIX big-endian

Motorola m68k All big-endian 

您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP