• 字节序:大端与小端


    转自字节序:大端与小端


    本文背景:

    对于嵌入式工程师来说,不仅要熟悉各种数据类型,还需要熟谙各种数据在内存中的表达形式。软件高手们通过内存与指针微妙的配合,总能摩擦出惊艳的火花。在讨论数据的存储结构时,必然会涉及到大端模式(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]),之后再进行发送才可以得到正确的数据。


  • 相关阅读:
    CF1539 VP 记录
    CF1529 VP 记录
    CF875C National Property 题解
    CF1545 比赛记录
    CF 1550 比赛记录
    CF1539E Game with Cards 题解
    CF1202F You Are Given Some Letters... 题解
    vmware Linux虚拟机挂载共享文件夹
    利用SOLR搭建企业搜索平台 之九(solr的查询语法)
    利用SOLR搭建企业搜索平台 之四(MultiCore)
  • 原文地址:https://www.cnblogs.com/noble/p/4144121.html
Copyright © 2020-2023  润新知