zoukankan      html  css  js  c++  java
  • 字节序:大端与小端

    转自字节序:大端与小端


    本文背景:

    对于嵌入式工程师来说,不仅要熟悉各种数据类型,还需要熟谙各种数据在内存中的表达形式。软件高手们通过内存与指针微妙的配合,总能摩擦出惊艳的火花。在讨论数据的存储结构时,必然会涉及到大端模式(Big-Endian)和小端模式(Little-Endian)的问题。平时编程时对于这个概念并不会有太多接触,但是在通讯协议的处理、可移植性方面就必须要考虑到字节序的问题。以下就来讨论这个问题。

    一、大端和小端的起源

    关于大端小端名词的由来,有一个有趣的故事,来自于Jonathan Swift的《格利佛游记》:

    Lilliput和Blefuscu这两个强国在过去的36个月中一直在苦战。战争的原因:大家都知道,吃鸡蛋的时候,原始的方法是打破鸡蛋较大的一端,可以那时的皇帝的祖父由于小时侯吃鸡蛋,按这种方法把手指弄破了,因此他的父亲,就下令,命令所有的子民吃鸡蛋的时候,必须先打破鸡蛋较小的一端,违令者重罚。然后老百姓对此法令极为反感,期间发生了多次叛乱,其中一个皇帝因此送命,另一个丢了王位,产生叛乱的原因就是另一个国家Blefuscu的国王大臣煽动起来的,叛乱平息后,就逃到这个帝国避难。据估计,先后几次有11000余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋。这个其实讽刺当时英国和法国之间持续的冲突。Danny Cohen一位网络协议的开创者,第一次使用这两个术语指代字节顺序,后来就被大家广泛接受。

    二、什么是大端和小端

    Big-Endian和Little-Endian的定义如下:
    1) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
    2) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

    举一个例子,比如数字0×12 34 56 78在内存中的表示形式为:

    1)大端模式:

    低地址 ————————> 高地址

    0×12 | 0×34 | 0×56 | 0×78

    2)小端模式:

    低地址 ————————-> 高地址

    0×78 | 0×56 | 0×34 | 0×12

    可见,大端模式和字符串的存储模式类似。

    大端小端没有谁优谁劣,各自优势便是对方劣势:

    大端:容易判断正负(offset(0));

    小端:易于进行数据类型转换,1、2、4字节的存储方式一样。

     三、如何判断机器的字节序

     可以编写一个小的测试程序来判断机器的字节序:

    BOOLIsBigEndian()
    {
        inta = 0x1234;
        charb =  *(char*)&a;  // 取b等于a的低地址部分
    if( b == 0x12)
    {
        returnTRUE;
    }
    returnFALSE;
    }

    网上还有一种使用联合体的方法:

    BOOLIsBigEndian()
    {
        unionNUM
    {
        inta;
        charb;
    }num;
    num.a = 0x1234;
    if( num.b == 0x12 )
    {
        returnTRUE;
    }
    returnFALSE;
    }

    四、常见的字节序

     一般操作系统都是小端,而通讯协议是大端的。

    4.1 常见CPU的字节序

     Big Endian : PowerPC、IBM、Sun

    Little Endian : x86、DEC

    ARM既可以工作在大端模式,也可以工作在小端模式。

     

    4.2 常见文件的字节序

    Adobe PS – Big Endian

    BMP – Little Endian

    DXF(AutoCAD) – Variable

    GIF – Little Endian

    JPEG – Big Endian

    MacPaint – Big Endian

    RTF – Little Endian

    另外,Java和所有的网络通讯协议都是使用Big-Endian的编码。

    五、如何进行转换

    对于字数据(16位):

    #define BigtoLittle16(A)   ((( (uint16)(A) & 0xff00) >> 8)    | 
                                (( (uint16)(A) & 0x00ff) << 8))

    对于双字数据(32位):

    #define BigtoLittle32(A)   ((( (uint32)(A) & 0xff000000) >> 24) | 
                               (( (uint32)(A) & 0x00ff0000) >> 8)   | 
                               (( (uint32)(A) & 0x0000ff00) << 8)   | 
                               (( (uint32)(A) & 0x000000ff) << 24))<span style="background-color: rgb(255, 255, 255); font-family: Arial, Helvetica, sans-serif;"> </span>

    六、实际中的例子

    虽然很多时候,字节序的工作已由编译器完成了,但是在一些小的细节上,仍然需要工程师去仔细揣摩考虑,尤其是在以太网通讯、MODBUS通讯、软件移植性方面。
    这里,我举一个MODBUS通讯的例子。
    在MODBUS中,数据需要组织成数据报文,该报文中的数据都是大端模式,即低地址存高位,高地址存低位。

    假设有一16位缓冲区m_RegMW[256],因为是在x86平台上,所以内存中的数据为小端模式:

    m_RegMW[0].low、m_RegMW[0].high、m_RegMW[1].low、m_RegMW[1].high……

    为了方便讨论,假设m_RegMW[0] = 0×3456; 在内存中为0×56、0×34。

    现要将该数据发出,如果不进行数据转换直接发送,此时发送的数据为0×56,0×34。而Modbus是大端的,会将该数据解释为0×5634而非原数据0×3456,此时就会发生灾难性的错误。

    所以,在此之前,需要将小端数据转换成大端的,即进行高字节和低字节的交换,此时可以调用步骤五中的函数BigtoLittle16(m_RegMW[0]),之后再进行发送才可以得到正确的数据。


  • 相关阅读:
    Building Apache Thrift on CentOS 6.5
    ToStringBuilder 学习
    对List中对象的去重
    MyEclipse启动Tomcat服务器时老是跳到Debug调试上
    JS 实现点击展开菜单
    详解公钥、私钥、数字证书的概念 转载
    eclipse svn 忽略 target目录 等等... 我用的后边的方法 (转载)
    Log4j XML 配置
    JS完成改变新闻字体大中小的显示
    Javascript 简单学习
  • 原文地址:https://www.cnblogs.com/noble/p/4144121.html
Copyright © 2011-2022 走看看