大小端问题解析

 1 static union { char c[4]; unsigned long l; } endian_test = { { 'l', '?', '?', 'b' } };  
 2 #define ENDIANNESS ((char)endian_test.l)  
 3 
 4 if ('l' == ENDIANNESS )/* 小端情况 */  
 5 {  
 6     do something;  
 7 }  
 8 else  
 9 {  
10     do something;  
11 }  

不同机器内部对变量的字节存储顺序不同,的采用大端模式(big-endian),有的采用小端模式(little-endian)。
大端模式是指高字节数据存放在低地址处,低字节数据放在高地址处。
小端模式是指低字节数据存放在低地址处,高字节数据放在高地址处。

在网络上传输数据时,由于数据传输的两端可能对应不同的硬件平台,采用的存储字节顺序也可能不一致,因此 TCP/IP 协议规定了在网络上必须采用网络字节顺序(也就是大端模式) 。
通过对大小端的存储原理分析可发现,对于 char 型数据,由于其只占一个字节,所以不存在这个问题,这也是一般情况下把数据缓冲区定义成 char 类型 的原因之一。对于 IP 地址、端口号等非 char 型数据,必须在数据发送到网络上之前将其转换成大端模式,在接收到数据之后再将其转换成符合接收端主机的存储模式。

Linux 系统为大小端模式的转换提供了 4 个函数,输入 man byteorder 命令可得函数原型:
 
 #include <arpa/inet.h> 
 
uint32_t htonl(uint32_t hostlong); 
 
uint16_t htons(uint16_t hostshort); 
 
uint32_t ntohl(uint32_t netlong); 
 
uint16_t ntohs(uint16_t netshort); 
#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

htonl 表示 host to network long ,用于将主机 unsigned int 型数据转换成网络字节顺序;
htons 表示 host to network short ,用于将主机 unsigned short 型数据转换成网络字节顺序;
ntohl、ntohs 的功能分别与 htonl、htons 相反。

=======================================================================
大小端问题
    最近工作中,有两次遇到大小端问题,所以花时间写这篇日志,总结一下。

1.      实际需求
         (1) 前段时间写了一个修复损坏的gzip文件的tool,在Linux Server上编译运行没有问题。但是在Solaris Server上运编译运行,结果总是和预期的不一致,跟踪发现是由大小端问题导致的;
        (2) 最近在写一个跨平台的编译脚本,编译参数里有目标可执行程序运行平台大小端这个参数;

2.     大小端解析
        端模式出自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) 下面贴一个很简单的判断大小端的函数
[cpp] view plain copy
print?
int checkCPUendian()//返回1,为小端;反之,为大端; 

    union 
    { 
        unsigned int  a; 
        unsigned char b; 
    }c; 
    c.a = 1; 
    return 1 == c.b; 

       (2) 大端模式处理器的字节序到网络字节序不需要转换,此时ntohs(n)=n,ntohl =n;而小端模式处理器的字节序到网络字节必须要进行转换(同理,有时候需要将大端字节顺序转换成小端字节顺序,也用这个函数,因为这个函数本来就是用来颠倒字节顺序的),转换如下:
 

posted @ 2017-12-01 00:42  elseliving的记录  阅读(431)  评论(0编辑  收藏  举报