Big-endia与Little-endian

来源:互联网 发布:中国高铁领跑世界 知乎 编辑:程序博客网 时间:2024/06/03 18:13
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),而littleendian则是低地址存放最低有效字节(LSB)。用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:

Big Endian
一个Word中的高位的Byte放在内存中这个Word区域的低地址处

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

Little Endian
一个Word中的低位的Byte放在内存中这个Word区域的低地址处

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

      从上面两图可以看出,采用bigendian方式存储数据是符合我们人类的思维习惯的。必须注意的是:一个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位即一个字节为单位的.简单的说,littleendian把低位存放到高位,而big endian把低位存放到低位. 现在主流的CPU,intel系列的是采用的littleendian的格式存放数据,而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方式传送数据,也就是说,假设现在是在一个字节顺序是littleendian的机器上传送数据,要求传送的数据是0XCEFABOBO,那么你就要以0XBOBOFACE的顺序在unsignedint中存放这个数据,只有这样才能保证存放的顺序满足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 ;
   }

原创粉丝点击