談到字節排序的問題,必然牽涉到兩大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列 採用big endian方式存儲數據,而x86系列則採用little endian方式存儲數據。ARM同時支持 big和little,實際應用中通常使用little endian。那麼究竟什麼是big endian,什麼又是little endian呢?
其實big endian是指低地址存放最高有效字節(MSB),而little endian則是低地址存放最低有效字節(LSB)。用文字說明可能比較抽象,下面用圖像加以說明。比如數字0x12345678在兩種不同字節序CPU 中的存儲順序如下所示:
Big Endian
一個Word中的高位的Byte放在內存中這個Word區域的低地址處
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little Endian
一個Word中的低位的Byte放在內存中這個Word區域的低地址處
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
從上面兩圖可以看出,採用big
endian方式存儲數據是符合我們人類的思維習慣的。必須注意的是:一個Word的長度是16位,一個Byte的長度是8位。如果一個數超過一個
Word的長度,必須先按Word分成若干部分,然後每一部分(即每個Word內部)按Big-Endian或者Little-Endian的不同操作來
處理字節。
一個例子:
如果我們將0x1234abcd寫入到以0x0000開始的內存中,則結果爲
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x34 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
需要特別說明的是,以上假設機器是每個內存單元以8位即一個字節爲單位的.簡單的說,little
endian把低位存放到高位,而big endian把低位存放到低位. 現在主流的CPU,intel系列的是採用的little
endian的格式存放數據,而motorola系列的CPU採用的是big endian.
以下是判斷字節存儲順序的可移植的C語言代碼:
/*可移植的用於判斷存儲格式是
little endian還是big ednian的C代碼*/
#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[i], u.Char[i]);
}
return 0 ;
}
在網絡編程中,TCP/IP統一採用big endian方式傳送數據,也就是說,假設現在是在一個字節順序是little
endian的機器上傳送數據,要求傳送的數據是0XCEFABOBO,那麼你就要以0XBOBOFACE的順序在unsigned
int中存放這個數據,只有這樣才能保證存放的順序滿足TCP/IP的字節順序要求.很多時候,需要自己編寫應用層的協議,字節順序的概念在這個時候就顯
得及其的重要了.
下面給出的是在big endian和little endian中相互轉換的代碼,C語言強大的位操作的能力在這裏顯示了出來:
/*在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[i] = data % 256 ;
// 或者可以:
// cmd[i] = 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[i];
}
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[i]);
}
// 再以相反的順序保存數據到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 ;
}