(转)大小端问题

大小端解析

        端模式出自Jonathan Swift书写的《格列佛游记》一书,这本书根据将鸡蛋敲开的方法不同将所有的人分为两类,从圆头开始将鸡蛋敲开的人被归为Big Endian,从尖头开始将鸡蛋敲开的人被归为Littile Endian。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。
        在计算机业Big Endian和Little Endian也几乎引起一场战争。在计算机业界,Endian表示数据在存储器中的存放顺序。
        大端:高位存在低地址,低位存在高地址;
        小端:高位存在高地址,低位存在低地址;(intel的x86,ARM普遍都是属于小端)

        举个例子,从内存地址0x0000开始有以下数据
        0x0000    0x12
        0x0001    0x34
        0x0002    0xab
        0x0003    0xcd
    
        如果我们去读取一个地址为0x0000的四个字节变量:
                若字节序为big-endian,则读出结果为0x1234abcd;
                若字节序位little-endian,则读出结果为0xcdab3412.

        如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为:
                         big-endian      little-endian
        0x0000          0x12                  0xcd
        0x0001          0x23                  0xab
        0x0002          0xab                  0x34
        0x0003          0xcd                  0x12
        
        Intelx86系列以及ARM系列CPU都是little-endian的字节序.

3.    大小端问题的解决
       (1) 下面贴一个很简单的判断大小端的函数

  1. int checkCPUendian()//返回1,为小端;反之,为大端;  
  2. {  
  3.     union  
  4.     {  
  5.         unsigned int  a;  
  6.         unsigned char b;  
  7.     }c;  
  8.     c.a = 1;  
  9.     return 1 == c.b;  
  10. }  

 

       (2) 网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。大端模式处理器的字节序到网络字节序不需要转换,此时ntohs(n)=n,ntohl =n;而小端模式处理器的字节序到网络字节必须要进行转换(同理,有时候需要将大端字节顺序转换成小端字节顺序,也用这个函数,因为这个函数本来就是用来颠倒字节顺序的),转换如下:

  

    1. #if defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN)  
    2.   
    3.   
    4.    #define htons(A)  (A)  
    5.    #define htonl(A)  (A)  
    6.    #define ntohs(A)  (A)  
    7.    #define ntohl(A)  (A)  
    8.   
    9.   
    10. #elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)  
    11.   
    12.   
    13.    #define htons(A)  ((((uint16_t)(A) & 0xff00) >> 8 ) | \\  
    14.                       (((uint16_t)(A) & 0x00ff) << 8 ))  
    15.    #define htonl(A)  ((((uint32_t)(A) & 0xff000000) >> 24)  | \\  
    16.                       (((uint32_t)(A) & 0x00ff0000) >> 8 )  | \\  
    17.                       (((uint32_t)(A) & 0x0000ff00) << 8 )  | \\  
    18.                       (((uint32_t)(A) & 0x000000ff) << 24))  
    19.    #define ntohs     htons  
    20.    #define ntohl     htohl  
    21.   
    22.   
    23. #else  
    24.   
    25.   
    26.    #error Either BIG_ENDIAN or LITTLE_ENDIAN must be #defined, but not both.  
    27.   
    28.   
    29. #endif  

 

 

转自http://blog.csdn.net/yasaken/article/details/7243757 讲大小端讲得比较清楚, MARK之。

另外有一点还没讲到而需要特别注意的是,大小端对移位运算的影响。

比如, 移位运算时,移动的是低字节还是低地址处的值?比如0x1234,对于小端系统而言,因为低地址存低字节,所以不存在这个问题,但是对于大端系统而言,由于高地址存低字节,低地址存高字节,所以如果移动的是低字节,那么右移的结果应该为0x0012,如果移动的是低地址,那右移结果应该是0x3400。结果应该是哪个?

   在回答这个问题的时候很容易陷入对字节序的转换的误区。大小端是在内存中存储多字节时的不同方式,移位运算(也包括其他的运算)移的是寄存器中的内容,不是内存中的内存。所以与大小端无关。在运算的时候CPU从内存将数据读出到寄存器的时候会根据机器本身的大小端情况完成转换,在运算的时候就无需考虑大小端的问题。

posted @ 2014-08-30 12:52  woainilsr  阅读(767)  评论(0)    收藏  举报