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語言強大的位操作的能力在這裏顯示了出來:
參考資料:<<C: A Reference Manual>>
/********************************************************************
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;
}
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空間。