little endian和big endian

 

1.故事的起源 

“endian”這個詞出自《格列佛遊記》。小人國的內戰就源於吃雞蛋時是究竟從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開,由此曾發生過六次叛亂,其中一個皇帝送了命,另一個丟了王位。 

我們一般將endian翻譯成“字節序”,將big endian和little endian稱作“大尾”和“小尾”。 

2.什麼是Big Endian和Little Endian? 

在設計計算機系統的時候,有兩種處理內存中數據的方法。一種叫爲little-endian,存放在內存中最低位的數值是來自數據的最右邊部分(也就是數據的最低位部分)。 

比如某些文件需要在不同平臺處理,或者通過Socket通信。這方面我們可以藉助ntohl(), ntohs(), htonl(), and htons()函數進行格式轉換, 
個人補充:一個操作數作htonl或ntohl結果不一定相同,當機器字節序跟網絡字節序剛好是僅僅big endian和little endian的區別時是相同的。 

3. 如何理解Big Endian和Little Endian 
   
舉個例子: 
int a = 1; 
a這個數本身的16進製表示是0x00 00 00 01 
在內存中怎麼存儲呢? 
如果你的CPU是intel x86架構的(基本上就是通常我們說的奔騰cpu),那麼就是0x01 0x00 0x00 0x00 , 這也就是所謂的little-endian, 低字節存放在內存的低位. 
如果你的CPU是老式AMD系列的(很老很老的那種,因爲最新的AMD系列已經是x86架構了), 它的字節序就是big-endian, 其內存存儲就是 
0x00 0x00 0x00 0x01在內存中從高字節開始存放。 
現在世界上絕大多數的CPU都是little-endian。 


4. big-endian和little-endian實例 

 以下是判斷字節存儲順序的可移植的C語言代碼: 


/********************************************************************
created: 2006-9-5
filename:  test.cpp
author: 李創

purpose: 可移植的用於判斷存儲格式是
                little endian還是big ednian的C代碼
                取自<<C: A Reference Manual>>
*********************************************************************/

#include <stdio.h>

union
{
long Long;
char Char[sizeof(long)];
}u;

int main()
{
u.Long = 1;

if (u.Char[0] == 1)
{
printf("Little Endian!/n");
}
else if (u.Char[sizeof(long) - 1] == 1)
{
printf("Big Endian!/n");
}
else
{
printf("Unknown Addressing!/n");
}

    printf("Now, Let's look at every byte in the memory!/n");
    for (int i = 0; i < sizeof(long); ++i)
    {
        printf("[%x] = %x/n", &u.Char
, u.Char);
    }

return 0;
}



 
很多人認爲掌握這個知識是不必要,其實不然.在網絡編程中,TCP/IP統一採用big endian方式傳送數據,也就是說,假設現在是在一個字節順序是little endian的機器上傳送數據,要求傳送的數據是0XCEFABOBO,那麼你就要以0XBOBOFACE的順序在unsigned int中存放這個數據,只有這樣才能保證存放的順序滿足TCP/IP的字節順序要求.很多時候,需要自己編寫應用層的協議,字節順序的概念在這個時候就顯得及其的重要了. 
       下面給出的是在big endian和little endian中相互轉換的代碼,C語言強大的位操作的能力在這裏顯示了出來: 


/********************************************************************
created: 2006-9-5
filename:  get32put32.cpp
author: 李創

purpose: 在little endian和big ednian之間相互轉化數據的演示代碼

*********************************************************************/


#include <stdio.h>

const unsigned char SIZE_OF_UNSIGNEDINT  = sizeof(unsigned int);
const unsigned char SIZE_OF_UNSIGNEDCHAR = sizeof(unsigned char);

void put_32(unsigned char *cmd, unsigned int data)
{
    int i;
    for (i = SIZE_OF_UNSIGNEDINT - 1; i >= 0; --i)
    {
        cmd
 = data % 256;
        // 或者可以:
        //cmd
 = data & 0xFF;
        data = data >> 8;
    }
}

unsigned int get_32(unsigned char *cmd)
{
    unsigned int  ret;
    int i;

    for (ret = 0, i = SIZE_OF_UNSIGNEDINT - 1; i >= 0; --i)
    {
        ret  = ret << 8;
        ret |= cmd
;        
    }

    return ret;
}

int main(void)
{
    unsigned char cmd[SIZE_OF_UNSIGNEDINT];
    unsigned int data, ret;
    unsigned char *p;
    int i;

    data = 0x12345678;
    printf("data = %x/n", data);
    // 以字節爲單位打印出數據
    p = (unsigned char*)(&data);
    for (i = 0; i < SIZE_OF_UNSIGNEDINT; ++i)
    {
        printf("%x", *p++);
    }
    printf("/n");

    // 以相反的順序存放到cmd之中
    put_32(cmd, data);
    for (i = 0; i < SIZE_OF_UNSIGNEDINT; ++i)
    {
        printf("cmd[%d] = %x/n", i, cmd
);
    }

    // 再以相反的順序保存數據到ret中
    // 保存之後的ret數值應該與data相同
    ret = get_32(cmd);
    printf("ret = %x/n", ret);
    p = (unsigned char*)(&ret);
    for (i = 0; i < SIZE_OF_UNSIGNEDINT; ++i)
    {
        printf("%x", *p++);
    }
    printf("/n");

    return 0;
}

參考資料:<<C: A Reference Manual>>


5.如何判斷系統是Big Endian還是Little Endian? 

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

 

 

 

轉自:小白的QQ空間。

發佈了37 篇原創文章 · 獲贊 1 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章