• 大小端问题解析


     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;而小端模式处理器的字节序到网络字节必须要进行转换(同理,有时候需要将大端字节顺序转换成小端字节顺序,也用这个函数,因为这个函数本来就是用来颠倒字节顺序的),转换如下:
     

  • 相关阅读:
    (转)ASP.NET-关于Container dataitem 与 eval方法介绍
    WinForm控件复杂数据绑定常用数据源(对Combobox,DataGridView等控件DataSource赋值的多种方法)
    转载 C# BindingSource
    repeater控件实现分页
    JS操作SELECT方法
    C#中的Invoke
    ios深拷贝,浅拷贝,拷贝自定义对象的简单介绍(转)
    IOS的一些尺寸
    ASP.NET多线程下使用HttpContext.Current
    一些xcode5.1创建的工程在xcode6.0下不能编译的问题
  • 原文地址:https://www.cnblogs.com/elseliving/p/7528811.html
Copyright © 2020-2023  润新知