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

鴨脖發表於2013-06-13

Byte Endian是指位元組在記憶體中的組織,所以也稱它為ByteOrdering,或Byte Order。
     對於資料中跨越多個位元組的物件, 我們必須為它建立這樣的約定:
(1) 它的地址是多少?
(2) 它的位元組在記憶體中是如何組織的?
    針對第一個問題,有這樣的解釋:
    對於跨越多個位元組的物件,一般它所佔的位元組都是連續的,它的地址等於它所佔位元組最低地址。(連結串列可能是個例外, 但連結串列的地址可看作連結串列頭的地址)。
    比如: int x, 它的地址為0x100。 那麼它佔據了記憶體中的Ox100, 0x101, 0x102, 0x103這四個位元組(32位系統,所以int佔用4個位元組)。
    上面只是記憶體位元組組織的一種情況: 多位元組物件在記憶體中的組織有一般有兩種約定。 考慮一個W位的整數。
    它的各位表達如下:[Xw-1, Xw-2, ... , X1, X0],它的
    MSB (Most Significant Byte, 最高有效位元組)為 [Xw-1, Xw-2, ... Xw-8];
    LSB (Least Significant Byte, 最低有效位元組)為 [X7,X6,..., X0]。
    其餘的位元組位於MSB, LSB之間。

LSB和MSB誰位於記憶體的最低地址, 即誰代表該物件的地址?
這就引出了大端(Big Endian)與小端(Little Endian)的問題。
如果LSB在MSB前面, 既LSB是低地址, 則該機器是小端; 反之則是大端。
DEC (Digital Equipment Corporation,現在是Compaq公司的一部分)和Intel的機器(X86平臺)一般採用小端。
IBM, Motorola(Power PC), Sun的機器一般採用大端。
當然,這不代表所有情況。有的CPU即能工作於小端, 又能工作於大端, 比如ARM, Alpha,摩托羅拉的PowerPC。 具體情形參考處理器手冊。
具體這類CPU是大端還是小端,應該和具體設定有關。
(如,Power PC支援little-endian位元組序,但在預設配置時是big-endian位元組序)
一般來說,大部分使用者的作業系統(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。
所以說,Little Endian還是Big Endian與作業系統和晶片型別都有關係。

Linux系統中,你可以在/usr/include/中(包括子目錄)查詢字串BYTE_ORDER(或
_BYTE_ORDER, __BYTE_ORDER),確定其值。BYTE_ORDER中文稱為位元組序。這個值一般在endian.h或machine/endian.h檔案中可以找到,有時在feature.h中,不同的作業系統可能有所不同。

         big endian是指低地址存放最高有效位元組(MSB),而little endian則是低地址存放最低有效位元組(LSB)。
         用文字說明可能比較抽象,下面用影像加以說明。比如數字0x12345678在兩種不同位元組序CPU中的儲存順序如下所示:

Big Endian

   低地址                                           高地址
   ----------------------------------------->
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     12    |      34    |    56      |    78    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian

   低地址                                           高地址
   ----------------------------------------->
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     78    |      56    |    34      |    12    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        從上面兩圖可以看出,採用big endian方式儲存資料是符合我們人類的思維習慣的.
        為什麼要注意位元組序的問題呢?你可能這麼問。當然,如果你寫的程式只在單機環境下面執行,並且不和別人的程式打交道,那麼你完全可以忽略位元組序的存在。但是,如果你的程式要跟別人的程式產生互動呢?在這裡我想說說兩種語言。C/C++語言編寫的程式裡資料儲存順序是跟編譯平臺所在的CPU相關的,而J***A編寫的程式則唯一採用big endian方式來儲存資料。試想,如果你用C/C++語言在x86平臺下編寫的程式跟別人的J***A程式互通時會產生什麼結果?就拿上面的0x12345678來說,你的程式傳遞給別人的一個資料,將指向0x12345678的指標傳給了J***A程式,由於J***A採取big endian方式儲存資料,很自然的它會將你的資料翻譯為0x78563412。什麼?竟然變成另外一個數字了?是的,就是這種後果。因此,在你的C程式傳給J***A程式之前有必要進行位元組序的轉換工作。
     無獨有偶,所有網路協議也都是採用big endian的方式來傳輸資料的。所以有時我們也會把big endian方式稱之為網路位元組序。當兩臺採用不同位元組序的主機通訊時,在傳送資料之前都必須經過位元組序的轉換成為網路位元組序後再進行傳輸。ANSI C中提供了下面四個轉換位元組序的巨集。
·BE和LE一文的補完
        我在8月9號的《Big Endian和Little Endian》一文中談了位元組序的問題,原文見上面的超級連結。可是有朋友仍然會問,CPU儲存一個位元組的資料時其位元組內的8個位元之間的順序是否也有big endian和little endian之分?或者說是否有位元序的不同?
     實際上,這個位元序是同樣存在的。下面以數字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  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


     實際上,由於CPU儲存資料操作的最小單位是一個位元組,其內部的位元序是什麼樣對我們的程式來說是一個黑盒子。也就是說,你給我一個指向0xB4這個數的指標,對於big endian方式的CPU來說,它是從左往右依次讀取這個數的8個位元;而對於little endian方式的CPU來說,則正好相反,是從右往左依次讀取這個數的8個位元。而我們的程式通過這個指標訪問後得到的數就是0xB4,位元組內部的位元序對於程式來說是不可見的,其實這點對於單機上的位元組序來說也是一樣的。
     那可能有人又會問,如果是網路傳輸呢?會不會出問題?是不是也要通過什麼函式轉換一下位元序?嗯,這個問題提得很好。假設little endian方式的CPU要傳給big endian方式CPU一個位元組的話,其本身在傳輸之前會在本地就讀出這個8位元的數,然後再按照網路位元組序的順序來傳輸這8個位元,這樣的話到了接收端不會出現任何問題。而假如要傳輸一個32位元的數的話,由於這個數在littel endian方儲存時佔了4個位元組,而網路傳輸是以位元組為單位進行的,little endian方的CPU讀出第一個位元組後傳送,實際上這個位元組是原數的LSB,到了接收方反倒成了MSB從而發生混亂。

【用函式判斷系統是Big Endian還是Little Endian】
bool IsBig_Endian()
//如果位元組序為big-endian,返回true;
//反之為   little-endian,返回false
{
    unsigned short test = 0x1122;
    if(*( (unsigned char*) &test ) == 0x11)
       return TRUE;
else
    return FALSE;

}//IsBig_Endian()

相關文章