Big endian and Little endian

本文详细解释了Big Endian和Little Endian两种字节序的区别,包括它们如何在内存中存储多字节数据,并提供了一段C语言代码示例用于判断当前机器的字节序类型。此外还介绍了网络编程中字节序的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Big endian and Little endian

谈到字节排序的问题,必然牵涉到两大CPU派系。那就是MotorolaPowerPC系列CPUIntelx86系列CPUPowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。ARM同时支持 biglittle,实际应用中通常使用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 ednianC代码*/
#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 endianlittle endian中相互转换的代码,C语言强大的位操作的能力在这里显示了出来
:
   /*
little endianbig ednian之间相互转化数据的演示代码
*/
       #include  < stdio.h >

       #define SIZE_OF_UNSIGNEDINT sizeof (unsigned  int )

       #define 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  =  0; i  <= SIZE_OF_UNSIGNEDINT  -   1 ;  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 = 0x%x " , data);

 

 

     // 以字节为单位打印出数据

       printf("/nthe default byte order is :/n");

     p = (unsigned  char * )( & data);

     for(i  =   0 ; i  <  SIZE_OF_UNSIGNEDINT;  ++ i)

      {

        printf( "0x%x " ,  * p ++ );

      }

     printf( " /n " );

 

 

     //以相反的顺序存放到cmd之中

       printf("/nafter convert,the byte order is :/n");

     put_32(cmd, data);

 

        p=cmd;

     for  (i  =   0 ; i  <  SIZE_OF_UNSIGNEDINT;  ++ i)

      {

        printf( "0x%x  " , *p++);

      }

 

 

     // 再以相反的顺序保存数据到ret

     // 保存之后的ret数值应该与data相同

     ret  =  get_32(cmd);

 

     printf( "/n/nret = 0x%x " , ret);

     p = (unsigned  char * )( & ret);

       printf("/nafter restore,the byte order is :/n");

     for(i  =   0 ; i  <  SIZE_OF_UNSIGNEDINT;  i++)

      {

        printf( "0x%x " ,  *p++);

      }

      printf( " /n " );

      return   0 ;

   }
 
     
为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。

      所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了下面四个转换字节序的宏。

      当前的存储器,多以byte为访问的最小单元,当一个逻辑上的整理必须分割为物理上的若干单元时就存在了先放谁后放谁的问题,于是endian的问题应运而生了,对于不同的存储方法,就有Big-endianLittle-endian两个描述.(这两个术语来自于 Jonathan Swift 的《《格利佛游记》其中交战的两个派别无法就应该从哪一端--小端还是大端--打开一个半熟的鸡蛋达成一致。在那个时代,Swift是在讽刺英国和法国之间的持续冲突,Danny Cohen,一位网络协议的早期开创者,第一次使用这两个术语来指代字节顺序,后来这个术语被广泛接纳了。)
      
存在如果说"word或者说字长根本就没关系",假设有一数据文件里面有N多数顺序排布,如果想以Little-Endian format 读入内存某区域,那么应该怎么读?怎么排?这样的问题是由于对于endian的实质理解的偏差,endian指的是当物理上的最小单元比逻辑上的最小单元小时,逻辑到物理的单元排布关系。这里的有一数据文件里面有N多数顺序排布,这个有一数据显然不是逻辑上的最小单元,而其中的“N多数的一个才是逻辑最小单元,于是可应用楼主表格中的原则排列,而“N多数之间的顺序则是由这“N多数的宿主决定的,比如是你写的程序,这个顺序由你决定.

    刚才谈到了,endian指的是当物理上的最小单元比逻辑上的最小单元小时,逻辑到物理的单元排布关系。咱们接触到的物理单元最小都是byte,在通信领域中,这里往往是bit,不过原理也是类似的。

    实践可以给你更多的经验,比如在一个嵌入式系统的通信协议中,从底层射频驱动到上层的协议栈全部需要实现,那么很可能遇到多个endian的问题,底层的bit序、协议层的byte序、应用层的byte序,这些都是不同的概念.

 

判断机器是大端还是小端得程序

endianess_judge.c

 

大端小端转换程序

endianness_convert.c

 

文档链接:

http://en.wikipedia.org/wiki/Endianness

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值