Big-endia與Little-endian

Big-endia與Little-endian - [Linux]
2008年03月01日 星期六 16:02

轉載地址:http://hi.baidu.com/rongjiang/blog/item/99b72a7aef44e2e92f73b3c0.html

 

      談到字節排序的問題,必然牽涉到兩大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 ;
   }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章