精华内容
下载资源
问答
  • 大端
    千次阅读
    2022-04-03 21:16:24

    一 什么是字节序

    字节(Byte)是计算机世界的计量单位,比如,一部电视剧是10G个字节(1GB),一张图片是1K个字节(1KB)。这些数据量有多有少,大多数时候,一个字节肯定是装不下的,这个时候我们就要用到多字节。在使用多字节的存的时候,就会涉及到一个顺序问题。
    在计算机中,字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。举例来说,数值0x2211使用两个字节储存:高位字节是0x22,低位字节是0x11:

    二 什么是大端序和小端序

    大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。简单来说,就是按照从低地址到高地址的顺序存放数据的高位字节到低位字节,就如同例子中的0X2211
    小端字节序:低位字节在前,高位字节在后,就是按照从低地址到高地址的顺序存放据的低位字节到高位字节。即以0x1122形式储存。
    举个栗子:如一个 int32 的数值存储在内存中的地址是,0x0A0B0C0D,数据是8bits为单位,则存储如下:

    更多相关内容
  •  在各种体系的计算机中通常采用的字节存储机制主要有两种: big-endian和little-endian,即大端模式和小端模式。  先回顾两个关键词,MSB和LSB:  MSB:Most Significant Bit ------- 最高有效位 LSB:Least ...
  • 开头讲个有关 大端小端的故事:  端模式(Endian)的这个词出自Jonathan Swift书写的《格列佛游记》。这本书根据将鸡蛋敲开的方法不同将所有的人分为两类,从圆头开始将鸡蛋敲开的人被归为Big Endian,从尖头...
  • 字节顺序此板条箱提供了方便的方法,可以按大端或小端顺序对数字进行编码和解码。 MIT或双重许可。文献资料安装此板条箱可与Cargo一起使用,位于。 像这样将其添加到您的Cargo.toml : [ dependencies ]byteorder = ...
  • 开头讲个有关 大端小端的故事:  端模式(Endian)的这个词出自Jonathan Swift书写的《格列佛游记》。这本书根据将鸡蛋敲开的方法不同将所有的人分为两类,从圆头开始将鸡蛋敲开的人被归为Big Endian,从尖头...
  • 关于字节序(大端法、小端法)的定义《UNXI网络编程》定义:术语“小端”和“大端”表示多字节值的哪一端(小端或大端)存储在该值的起始地址。小端存在起始地址,即是小端字节序;大端存在起始地址,即是大端字节序。 ...
  • int64be 编码/解码大端符号64位整数 npm install int64be用法 var int64be = require ( 'int64be' )var buf = int64be . encode ( - 42 ) // returns a 8 byte buffer with -42 encodedconsole . log ( int64be . ...
  • 运行程序,可判识并输出程序所在平台的存储方式,是大端还是小端。
  • little endian,big endian 小端存储、大端存储.zip
  • 所有协议数据的明文需要在尾部加上原始数据长度(int大端字节数4字节,协议数据的长度),再加上“ABCD”4个字母,然后按64位进行分组,不足64位的以“\0”补足,分组后的明文采用统一的加密方式生成密文,即采用 ...
  • mips-linux-gcc大端

    2017-09-18 16:30:33
    mips交叉编译器 32位 大端模式 (ubuntu12 gcc4.9.4生成) 大端模式 大端模式 大端模式 大端模式 大端模式 大端模式
  • 深刻理解大端模式与小端模式的概念,但我们如何判别当前系统为大端模式还是小端模式呢?
  • 分析了1000MW级THDF125/67型发电机大端盖的结构特点、检修工艺等,指出大端盖安装工艺不合理是造成漏氢的主要原因。通过调整、优化安装工艺,成功地消除了漏氢缺陷,提高了发电机运行的可靠性和安全性。
  • 用gdb-7.11编译的gdbserver,静态链接,大端mips架构 ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=062b51edc22a5ae78fc1f31debbc534c6...
  • 西门子PLC接口,大端和小-Endian的存储格式pdf,西门子PLC接口,大端和小-Endian的存储格式:本文介绍了SINUMERIK:PLC接口,和Little-Endian的Big-Endian的存储格式
  • 大端和小端

    千次阅读 2021-03-10 11:00:05
    一、什么是大端和小端 大端模式,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 小端模式,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 举个例子,比如数字 0x12 34 56...

    一、什么是大端和小端
    大端模式,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
    小端模式,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

    举个例子,比如数字 0x12 34 56 78(4个字节)在内存中的表示形式为:
    1)大端模式:
    低地址 -----------------> 高地址(数字高位存于低地址)
    0x12 | 0x34 | 0x56 | 0x78
    可见,大端模式和字符串的存储模式类似。
    2)小端模式:
    低地址 ------------------> 高地址(数字高位存于低地址)
    0x78 | 0x56 | 0x34 | 0x12

    3)下面是两个具体例子:

    16bit宽的数0x1234在Little-endian模式(以及Big-endian模式)CPU内存中的存放方式(假设从地址0x4000开始存放)为:

    内存地址小端模式存放内容大端模式存放内容
    0x40000x340x12
    0x40010x120x34

    32bit宽的数0x12345678在Little-endian模式以及Big-endian模式)CPU内存中的存放方式(假设从地址0x4000开始存放)为:

    内存地址小端模式存放内容大端模式存放内容
    0x40000x780x12
    0x40010x560x34
    0x40020x340x56
    0x40030x120x78

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

    小端模式 :强制转换数据不需要调整字节内容,1、2、4字节的存储方式一样。
    大端模式 :符号位的判定固定为第一个字节,容易判断正负。

    二、数组在大端小端情况下的存储:
      以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:
      Big-Endian: 低地址存放高位,如下:
    高地址
    ---------------
    buf[3] (0x78) – 低位
    buf[2] (0x56)
    buf[1] (0x34)
    buf[0] (0x12) – 高位
    ---------------
    低地址

    Little-Endian: 低地址存放低位,如下:
    高地址
    ---------------
    buf[3] (0x12) – 高位
    buf[2] (0x34)
    buf[1] (0x56)
    buf[0] (0x78) – 低位
    --------------
    低地址

    三、为什么会有大小端模式之分呢?
    这是因为在计算机中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8 bit。但是在C 语言中除了 8 bit 的char之外,还有 16 bit 的 short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型 x ,在内存中的地址为 0x0010,x 的值为0x1122,那么0x11位高字节,0x22位低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

    四、如何判断机器的字节序 (重点)
    一般都是通过 union 来测试的,下面这段代码可以用来测试一下你的编译器是大端模式还是小端模式:

     #include <stdio.h>
     int main (void)
     {
    	union
    	{
     		short i;
     		char a[2];
     	}u;
     	u.a[0] = 0x11;
     	u.a[1] = 0x22;
     	printf ("0x%x\n", u.i);  
     	//0x2211 为小端  0x1122 为大端
     	return 0;
     }
    
     输出结果:
     0x2211
    

    union 型数据所占的空间等于其最大的成员所占的空间。对 union 型的成员的存取都是相对于该联合体基地址的偏移量为 0 处开始,也就是联合体的访问不论对哪个变量的存取都是从 union 的首地址位置开始。

    联合是一个在同一个存储空间里存储不同类型数据的数据类型。这些存储区的地址都是一样的,联合里不同存储区的内存是重叠的,修改了任何一个其他的会受影响。

    参看:C语言再学习 – 结构和其他数据形式

    1. 共用体声明和共用体变量定义
      共用体(参考“共用体”百科词条)是一种特殊形式的变量,使用关键字union来定义
      共用体(有些人也叫"联合")声明和共用体变量定义与结构体十分相似。其形式为:
      union 共用体名{
      数据类型 成员名;
      数据类型 成员名;

      } 变量名;

    参看:

    共用体表示几个变量共用一个内存位置,在不同的时间保存不同的数据类型和不同长度的变量。在union中,所有的共用体成员共用一个空间,并且同一时间只能储存其中一个成员变量的值。

    下例表示声明一个共用体foo:

     union foo{
     /*“共用”类型“FOO”*/
     int i;    
     /*“整数”类型“i”*/
     char c;   
     /*“字符”类型“C”*/
     double k;  
     /*“双”精度类型“K”*/
     };
    

    再用已声明的共用体可定义共用体变量。例如,用上面说明的共用体定义一个名为bar的共用体变量, 可写成:
    union foo bar;
    在共用体变量bar中, 整型变量 i 和字符变量 c 共用同一内存位置。

    当一个共用体被声明时, 编译程序自动地产生一个变量, 其长度为联合中最大的变量长度的整数倍。以上例而言,最大长度是double数据类型,所以foo的内存空间就是double型的长度。

      union foo
         /*“共用”类型“FOO”*/
         {
         	char s[10];    
         	/*“字符”类型的数组“S”下面有“10”个元素*/
         	int i;        
         	/*“整数”类型i*/
         };
    

    1
    在这个union中,foo的内存空间的长度为12,是int型的3倍,而并不是数组的长度10。若把int改为double,则foo的内存空间为16,是double型的两倍。

    1. 共用体和结构体的区别
      1)共用体和结构体都是由多个不同的数据类型成员组成, 但在任何同一时刻, 共用体只存放了一个被选中的成员, 而结构体的所有成员都存在。
      2.)对于共用体的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构体的不同成员赋值是互不影响的。

    总结:

    恍然大悟,union 联合之前还是没有理解透。一开始不太理解,为什么给 a[0]、a[1] 赋值,i 没有定义啊,为什么会有值呢,或者值为什么不是随机数呢?现在明白了,我们为什么用 union 联合来测试大小端,在联合变量 u 中, 短整型变量 i 和字符数组 a 共用同一内存位置。给 a[0]、a[1] 赋值后,i 也是从同一内存地址读值的。

    知道这层关系后,那么通过强制类型转换,判断其实存储位置,也可以测试大小端了:

      #include <stdio.h>
         int main (void)
         {
         short i = 
         0x1122;
         char *a = (
         char*)(&i);
         printf (
         "0x%x\n", *(a + 0)); 
         //大端为 0x11 小端为 0x22
         printf (
         "0x%x\n", *(a + 
         1));
            return 0;
         }
    
     输出结果:
     0x22
     0x11
    

    1
    说明:上面两个例子,可以通过 if 语句来判断大小端,这里只是介绍方法。

    五、常见的字节序
    一般操作系统都是小端,而通讯协议是大端的。

    1)常见CPU的字节序

    Big Endian : PowerPC、IBM、Sun
    Little Endian : x86、DEC
    ARM既可以工作在大端模式,也可以工作在小端模式。

    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的编码。

    六、如何进行大小端转换(重点)
    第一种方法:位操作

     #include<stdio.h> 
     typedef 
     unsigned 
     int uint_32 ;  
     typedef 
     unsigned 
     short uint_16 ;  
     //16位
     #define BSWAP_16(x) \
      (uint_16)((((uint_16)(x) & 0x00ff) << 8) | \
    

    (((uint_16)(x) & 0xff00) >> 8)
    )
    //32位
    #define BSWAP_32(x)
    (uint_32)((((uint_32)(x) & 0xff000000) >> 24) |
    (((uint_32)(x) & 0x00ff0000) >> 8) |
    (((uint_32)(x) & 0x0000ff00) << 8) |
    (((uint_32)(x) & 0x000000ff) << 24)
    )
    //无符号整型16位
    uint_16 bswap_16(uint_16 x)
    {
    return (((uint_16)(x) & 0x00ff) <<
    8) |
    (((uint_16)(x) &
    0xff00) >>
    8) ;
    //无符号整型32位
    uint_32 bswap_32(uint_32 x)
    {
    return (((uint_32)(x) &
    0xff000000) >>
    24) |
    (((uint_32)(x) &
    0x00ff0000) >>
    8) |
    (((uint_32)(x) &
    0x0000ff00) <<
    8) |
    (((uint_32)(x) &
    0x000000ff) <<
    24) ;
    }
    int main(int argc,char *argv[])
    {
    printf(
    “------------带参宏-------------\n”);
    printf(
    “%#x\n”,BSWAP_16(
    0x1234)) ;
    printf(
    “%#x\n”,BSWAP_32(
    0x12345678));
    printf(
    “------------函数调用-----------\n”);
    printf(
    “%#x\n”,bswap_16(
    0x1234)) ;
    printf(
    “%#x\n”,bswap_32(
    0x12345678));
    return 0 ;
    }
    输出结果:
    ------------带参宏-------------
    0x3412
    0x78563412
    ------------函数调用-----------
    0x3412
    0x78563412

    这里有个思考?上面的哪个是转换为大端,哪个是转为小端了呢?

    参看:STM32开发 – 进制与字符串间的转换

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

    1)大端模式:

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

    0x12 | 0x34 | 0x56 | 0x78

    2)小端模式:

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

    0x78 | 0x56 | 0x34 | 0x12

    则:

    转换为大端:

    pPack[2] = (u8)((len >> 8) & 0xFF);
    pPack[3] = (u8)(len & 0xFF);

    转为为小端:

    pPack[2] = (u8)(len & 0xFF);

    pPack[3] = (u8)((len >> 8) & 0xFF);

    第二种方法:

    从软件的角度理解端模式

    使用 htonl, htons, ntohl, ntohs 等函数

    参看:百度百科–htonl ()函数

    参看:百度百科–htons ()函数

    查看:man htonl
    NAME

            htonl, htons, ntohl, ntohs - convert values between host 
     and network byte order
    
     SYNOPSIS
     #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);
     DESCRIPTION
    

    The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.
    The htons() function converts the unsigned short integer hostshort from host byte order to network byte order.
    The ntohl() function converts the unsigned integer netlong from network byte order to host byte order.
    The ntohs() function converts the unsigned short integer netshort from network byte order to host byte order.
    On the i386 the host byte order is Least Significant Byte first, whereas the network byte order, as used on the Internet, is Most
    Significant Byte first.

    1
    翻译:

    htonl() //32位无符号整型的主机字节顺序到网络字节顺序的转换(小端->>大端)
    htons() //16位无符号短整型的主机字节顺序到网络字节顺序的转换 (小端->>大端)
    ntohl() //32位无符号整型的网络字节顺序到主机字节顺序的转换 (大端->>小端)
    ntohs() //16位无符号短整型的网络字节顺序到主机字节顺序的转换 (大端->>小端)

    注,主机字节顺序,X86一般多为小端(little-endian),网络字节顺序,即大端(big-endian);
    举两个小例子:
    //示例一
    #include <stdio.h>
    #include <arpa/inet.h>
    int main (void)
    {
    union
    {
    short i;
    char a[2];
    }u;
    u.a[0] =
    0x11;
    u.a[
    1] =
    0x22;
    printf (
    “0x%x\n”, u.i);
    //0x2211 为小端 0x1122 为大端
    printf (
    “0x%.x\n”, htons (u.i));
    //大小端转换
    return
    0;
    }
    输出结果:
    0x2211
    0x1122
    //示例二
    #include <stdio.h>
    #include <arpa/inet.h>
    struct ST{
    short val1;
    short val2;
    };
    union U{
    int val;
    struct ST st;
    };
    int main(void)
    {
    int a = 0;
    union U u1, u2;
    a = 0x12345678;
    u1.val = a;
    printf( “u1.val is 0x%x\n”, u1.val);
    printf(“val1 is 0x%x\n”, u1.st.val1);
    printf(“val2 is 0x%x\n”, u1.st.val2);
    printf( “after first convert is: 0x%x\n”, htonl(u1.val));
    u2.st.val2 = htons(u1.st.val1);
    u2.st.val1 = htons(u1.st.val2);
    printf(“after second convert is: 0x%x\n”, u2.val);
    return 0;
    }
    输出结果:
    u1.val is 0x12345678
    val1 is 0x5678
    val2 is 0x1234
    after first convert is: 0x78563412
    after second convert is: 0x78563412

    在对普通文件进行处理也需要考虑端模式问题。在大端模式的处理器下对文件的32,16位读写操作所得到的结果与小端模式的处理器不同。单纯从软件的角度理解上远远不能真正理解大小端模式的区别。事实上,真正的理解大小端模式的区别,必须要从系统的角度,从指令集,寄存器和数据总线上深入理解,大小端模式的区别。

    以下内容了解:
    1、从系统的角度理解端模式
    先补充两个关键词,MSB和LSB:
      MSB:MoST Significant Bit ------- 最高有效位
    LSB:Least Significant Bit ------- 最低有效位

        处理器在硬件上由于端模式问题在设计中有所不同。从系统的角度上看,端模式问题对软件和硬件的设计带来了不同的影响,当一个处理器系统中大小端模式同时存在时,必须要对这些不同端模式的访问进行特殊的处理。
       PowerPC处理器主导网络市场,可以说绝大多数的通信设备都使用PowerPC处理器进行协议处理和其他控制信息的处理,这也可能也是在网络上的绝大多数协议都采用大端编址方式的原因。因此在有关网络协议的软件设计中,使用小端方式的处理器需要在软件中处理端模式的转变。而Pentium主导个人机市场,因此多数用于个人机的外设都采用小端模式,包括一些在网络设备中使用的PCI总线,Flash等设备,这也要求在硬件设计中注意端模式的转换。
       本文提到的小端外设是指这种外设中的寄存器以小端方式进行存储,如PCI设备的配置空间,NOR FLASH中的寄存器等等。对于有些设备,如DDR颗粒,没有以小端方式存储的寄存器,因此从逻辑上讲并不需要对端模式进行转换。在设计中,只需要将双方数据总线进行一一对应的互连,而不需要进行数据总线的转换。
       如果从实际应用的角度说,采用小端模式的处理器需要在软件中处理端模式的转换,因为采用小端模式的处理器在与小端外设互连时,不需要任何转换。而采用大端模式的处理器需要在硬件设计时处理端模式的转换。大端模式处理器需要在寄存器,指令集,数据总线及数据总线与小端外设的连接等等多个方面进行处理,以解决与小端外设连接时的端模式转换问题。在寄存器和数据总线的位序定义上,基于大小端模式的处理器有所不同。
       一个采用大端模式的32位处理器,如基于E500内核的MPC8541,将其寄存器的最高位msb(most significant bit)定义为0,最低位lsb(lease significant bit)定义为31;而小端模式的32位处理器,将其寄存器的最高位定义为31,低位地址定义为0。与此向对应,采用大端模式的32位处理器数据总线的最高位为0,最高位为31;采用小端模式的32位处理器的数据总线的最高位为31,最低位为0。         
       大小端模式处理器外部总线的位序也遵循着同样的规律,根据所采用的数据总线是32位,16位和8位,大小端处理器外部总线的位序有所不同。大端模式下32位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第31位,LSB是第24~31字段。小端模式下32位总线的msb是第31位,MSB是数据总线的第31~24位,lsb是第0位,LSB是7~0字段。大端模式下16位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第15位,LSB是第8~15字段。小端模式下16位总线的msb是第15位,MSB是数据总线的第15~7位,lsb是第0位,LSB是7~0字段。大端模式下8位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第7位,LSB是第0~7字段。小端模式下8位总线的msb是第7位,MSB是数据总线的第7~0位,lsb是第0位,LSB是7~0字段。
         由上分析,我们可以得知对于8位,16位和32位宽度的数据总线,采用大端模式时数据总线的msb和MSB的位置都不会发生变化,而采用小端模式时数据总线的lsb和LSB位置也不会发生变化。
         为此,大端模式的处理器对8位,16位和32位的内存访问(包括外设的访问)一般都包含第0~7字段,即MSB。小端模式的处理器对8位,16位和32位的内存访问都包含第7~0位,小端方式的第7~0字段,即LSB。由于大小端处理器的数据总线其8位,16位和32位宽度的数据总线的定义不同,因此需要分别进行讨论在系统级别上如何处理端模式转换。在一个大端处理器系统中,需要处理大端处理器对小端外设的访问。
    

    2、实际中的例子

       虽然很多时候,字节序的工作已由编译器完成了,但是在一些小的细节上,仍然需要去仔细揣摩考虑,尤其是在以太网通讯、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] = 0x3456; 在内存中为0x56、0x34。
    现要将该数据发出,如果不进行数据转换直接发送,此时发送的数据为0x56,0x34。而Modbus是大端的,会将该数据解释为0x5634而非原数据0x3456,此时就会发生灾难性的错误。所以,在此之前,需要将小端数据转换成大端的,即进行高字节和低字节的交换,此时可以调用步骤五中的函数BigtoLittle16(m_RegMW[0]),之后再进行发送才可以得到正确的数据。

    展开全文
  • 详解大端模式和小端模式

    千次阅读 2021-06-04 04:12:41
    //32位数据类型主机字节顺序到网络字节顺序的转换 其中互联网使用的网络字节顺序采用大端模式进行编址,而主机字节顺序根据处理器的不同而不同,如PowerPC处理器使用大端模式,而Pentuim处理器使用小端模式。 大端...

    七、如何进行转换

    对于字数据(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))

    八、从软件的角度理解端模式

    从软件的角度上,不同端模式的处理器进行数据传递时必须要考虑端模式的不同。如进行网络数据传递时,必须要考虑端模式的转换。在Socket接口编程中,以下几个函数用于大小端字节序的转换。

    #define ntohs(n)    //16位数据类型网络字节顺序到主机字节顺序的转换

    #define htons(n)    //16位数据类型主机字节顺序到网络字节顺序的转换

    #define ntohl(n)      //32位数据类型网络字节顺序到主机字节顺序的转换

    #define htonl(n)      //32位数据类型主机字节顺序到网络字节顺序的转换

    其中互联网使用的网络字节顺序采用大端模式进行编址,而主机字节顺序根据处理器的不同而不同,如PowerPC处理器使用大端模式,而Pentuim处理器使用小端模式。

    大端模式处理器的字节序到网络字节序不需要转换,此时ntohs(n)=n,ntohl = n;而小端模式处理器的字节序到网络字节必须要进行转换,此时ntohs(n) = __swab16(n),ntohl = __swab32(n)。__swab16与__swab32函数定义如下所示。

    #define ___swab16(x)

    {

    __u16 __x = (x);

    ((__u16)(

    (((__u16)(__x) & (__u16)0x00ffU) << 8) |

    (((__u16)(__x) & (__u16)0xff00U) >> 8) ));

    }

    #define ___swab32(x)

    {

    __u32 __x = (x);

    ((__u32)(

    (((__u32)(__x) & (__u32)0x000000ffUL) << 24) |

    (((__u32)(__x) & (__u32)0x0000ff00UL) << 8) |

    (((__u32)(__x) & (__u32)0x00ff0000UL) >> 8) |

    (((__u32)(__x) & (__u32)0xff000000UL) >> 24) ));

    }

    PowerPC处理器提供了lwbrx,lhbrx,stwbrx,sthbrx四条指令用于处理字节序的转换以优化__swab16和__swap32这类函数。此外PowerPC处理器中的rlwimi指令也可以用来实现__swab16和__swap32这类函数。

    在对普通文件进行处理也需要考虑端模式问题。在大端模式的处理器下对文件的32,16位读写操作所得到的结果与小端模式的处理器不同。单纯从软件的角度理解上远远不能真正理解大小端模式的区别。事实上,真正的理解大小端模式的区别,必须要从系统的角度,从指令集,寄存器和数据总线上深入理解,大小端模式的区别。

    九、从系统的角度理解端模式

    先补充两个关键词,MSB和LSB:

    MSB:MoST Significant Bit ------- 最高有效位

    LSB:Least Significant Bit ------- 最低有效位

    处理器在硬件上由于端模式问题在设计中有所不同。从系统的角度上看,端模式问题对软件和硬件的设计带来了不同的影响,当一个处理器系统中大小端模式同时存在时,必须要对这些不同端模式的访问进行特殊的处理。

    PowerPC处理器主导网络市场,可以说绝大多数的通信设备都使用PowerPC处理器进行协议处理和其他控制信息的处理,这也可能也是在网络上的绝大多数协议都采用大端编址方式的原因。因此在有关网络协议的软件设计中,使用小端方式的处理器需要在软件中处理端模式的转变。而Pentium主导个人机市场,因此多数用于个人机的外设都采用小端模式,包括一些在网络设备中使用的PCI总线,Flash等设备,这也要求在硬件设计中注意端模式的转换。

    本文提到的小端外设是指这种外设中的寄存器以小端方式进行存储,如PCI设备的配置空间,NOR FLASH中的寄存器等等。对于有些设备,如DDR颗粒,没有以小端方式存储的寄存器,因此从逻辑上讲并不需要对端模式进行转换。在设计中,只需要将双方数据总线进行一一对应的互连,而不需要进行数据总线的转换。

    如果从实际应用的角度说,采用小端模式的处理器需要在软件中处理端模式的转换,因为采用小端模式的处理器在与小端外设互连时,不需要任何转换。而采用大端模式的处理器需要在硬件设计时处理端模式的转换。大端模式处理器需要在寄存器,指令集,数据总线及数据总线与小端外设的连接等等多个方面进行处理,以解决与小端外设连接时的端模式转换问题。在寄存器和数据总线的位序定义上,基于大小端模式的处理器有所不同。

    一个采用大端模式的32位处理器,如基于E500内核的MPC8541,将其寄存器的最高位msb(most significant bit)定义为0,最低位lsb(lease significant bit)定义为31;而小端模式的32位处理器,将其寄存器的最高位定义为31,低位地址定义为0。与此向对应,采用大端模式的32位处理器数据总线的最高位为0,最高位为31;采用小端模式的32位处理器的数据总线的最高位为31,最低位为0。

    大小端模式处理器外部总线的位序也遵循着同样的规律,根据所采用的数据总线是32位,16位和8位,大小端处理器外部总线的位序有所不同。大端模式下32位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第31位,LSB是第24~31字段。小端模式下32位总线的msb是第31位,MSB是数据总线的第31~24位,lsb是第0位,LSB是7~0字段。大端模式下16位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第15位,LSB是第8~15字段。小端模式下16位总线的msb是第15位,MSB是数据总线的第15~7位,lsb是第0位,LSB是7~0字段。大端模式下8位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第7位,LSB是第0~7字段。小端模式下8位总线的msb是第7位,MSB是数据总线的第7~0位,lsb是第0位,LSB是7~0字段。

    由上分析,我们可以得知对于8位,16位和32位宽度的数据总线,采用大端模式时数据总线的msb和MSB的位置都不会发生变化,而采用小端模式时数据总线的lsb和LSB位置也不会发生变化。

    为此,大端模式的处理器对8位,16位和32位的内存访问(包括外设的访问)一般都包含第0~7字段,即MSB。小端模式的处理器对8位,16位和32位的内存访问都包含第7~0位,小端方式的第7~0字段,即LSB。由于大小端处理器的数据总线其8位,16位和32位宽度的数据总线的定义不同,因此需要分别进行讨论在系统级别上如何处理端模式转换。在一个大端处理器系统中,需要处理大端处理器对小端外设的访问。

    十、实际中的例子

    虽然很多时候,字节序的工作已由编译器完成了,但是在一些小的细节上,仍然需要去仔细揣摩考虑,尤其是在以太网通讯、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] = 0x3456; 在内存中为0x56、0x34。

    现要将该数据发出,如果不进行数据转换直接发送,此时发送的数据为0x56,0x34。而Modbus是大端的,会将该数据解释为0x5634而非原数据0x3456,此时就会发生灾难性的错误。所以,在此之前,需要将小端数据转换成大端的,即进行高字节和低字节的交换,此时可以调用步骤五中的函数BigtoLittle16(m_RegMW[0]),之后再进行发送才可以得到正确的数据。0b1331709591d260c1c78e86d0c51c18.png

    展开全文
  • 大端小端模式

    千次阅读 2021-02-19 23:37:29
    大端小端模式 一、什么是大小端模式 大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位...

    大端小端模式

    一、什么是大小端模式

    大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。

    小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。

    举个例子:

    以 unsigned int value = 0x12345678来表示,unsigned char buf[4]存放。

    Big-Endian: 低地址存放高位,如下:
       高地址
      ---------------
      buf[3] (0x78) -- 低位
      buf[2] (0x56)
      buf[1] (0x34)
      buf[0] (0x12) -- 高位
      ---------------
      低地址
      
    Little-Endian: 低地址存放低位,如下:
       高地址
      ---------------
      buf[3] (0x12) -- 高位
      buf[2] (0x34)
      buf[1] (0x56)
      buf[0] (0x78) -- 低位
      --------------
    低地址
    
    

    举个例子:
    java中的大小端转换

    import java.util.Arrays;
    
    class Test {
    
        public static void main(String[] args) {
            int a = 10;
            
            byte[] little = intToByteLittle(a);
            byte[] big = intToByteBig(a);
            
            System.out.println("little:"+ Arrays.toString(little)); // [10, 0, 0, 0]
            System.out.println("big:"+ Arrays.toString(big)); // [0, 0, 0, 10]
        }
    
        /**
         * 将int转为低字节在前,高字节在后的byte数组(小端)
         *
         * @param n int
         * @return byte[]
         */
        public static byte[] intToByteLittle(int n) {
            byte[] b = new byte[4];
            b[0] = (byte) (n & 0xff);
            b[1] = (byte) (n >> 8 & 0xff);
            b[2] = (byte) (n >> 16 & 0xff);
            b[3] = (byte) (n >> 24 & 0xff);
            return b;
        }
    
        /**
         * 将int转为高字节在前,低字节在后的byte数组(大端)
         *
         * @param n int
         * @return byte[]
         */
        public static byte[] intToByteBig(int n) {
            byte[] b = new byte[4];
            b[3] = (byte) (n & 0xff);
            b[2] = (byte) (n >> 8 & 0xff);
            b[1] = (byte) (n >> 16 & 0xff);
            b[0] = (byte) (n >> 24 & 0xff);
            return b;
        }
    }
    
    

    二、为什么要有大小端

    这个问题没有定论,就像汽车的方向盘有在左边的,也有在右边的。

    三、大小端与什么有关?

    编程语言和大小端没有关系,影响大小端的是 编译器/ CPU

    Android 是大端还是小端?

    上面讲过,CPU和编译器才是影响大端小端的关键,Android系统的碎片化,也说不好Android是大端还是小端。

    有必要知道它是大端还是小端吗? 没有必要。
    传一个字节会变吗? 不会。
    因为在数据转换的过程中就已经指定了是以大端还是小端的规则,相应的,如果传输给其它硬件或者后台,只需要按此规则解析即可。

    三、Java 转换大小端的工具类

    package com.demo.my.ble_client.utils;
    
    import java.util.Arrays;
    
    public class ByteUtils {
    
        /**
         * 两个byte[]是否相同
         * @param data1
         * @param data2
         * @return
         */
        public static boolean arrayEquals(byte[] data1, byte[] data2) {
            return Arrays.equals(data1, data2);
        }
    
        /**
         * 截取byte[]
         * @param data
         * @param position
         * @param length
         * @return
         */
        public static byte[] subArray(byte[] data, int position, int length) {
            byte[] temp = new byte[length];
            System.arraycopy(data, position, temp, 0, length);
            return temp;
        }
    
        /**
         * 解决java中byte输出可能为负数的问题。
         * @param b
         * @return
         */
        public static Integer byteToInteger(Byte b) {
            return 0xff & b;
        }
    
        /**
         * 拼接byte[] 和 byte[]
         *
         * @param bytes1
         * @param bytes2
         * @return
         */
        public static byte[] byteMerger(byte[] bytes1, byte[] bytes2) {
            byte[] bytes3 = new byte[bytes1.length + bytes2.length];
            System.arraycopy(bytes1, 0, bytes3, 0, bytes1.length);
            System.arraycopy(bytes2, 0, bytes3, bytes1.length, bytes2.length);
            return bytes3;
        }
    
        /**
         * 拼接byte 和 byte[]
         *
         * @param byte1
         * @param bytes2
         * @return
         */
        public static byte[] byteMerger(byte byte1, byte[] bytes2) {
            byte[] bytes3 = new byte[1 + bytes2.length];
            bytes3[0] = byte1;
            System.arraycopy(bytes2, 0, bytes3, 1, bytes2.length);
            return bytes3;
        }
    
        /**
         * 拼接byte[] 和 byte
         *
         * @param bytes1
         * @param byte2
         * @return
         */
        public static byte[] byteMerger(byte[] bytes1, byte byte2) {
            byte[] bytes3 = new byte[1 + bytes1.length];
            System.arraycopy(bytes1, 0, bytes3, 0, bytes1.length);
            bytes3[bytes3.length - 1] = byte2;
            return bytes3;
        }
    
        /**
         * 拼接三个数组
         *
         * @param bt1
         * @param bt2
         * @param bt3
         * @return
         */
        public static byte[] byteMerger3(byte[] bt1, byte[] bt2, byte[] bt3) {
            byte[] data = new byte[bt1.length + bt2.length + bt3.length];
            System.arraycopy(bt1, 0, data, 0, bt1.length);
            System.arraycopy(bt2, 0, data, bt1.length, bt2.length);
            System.arraycopy(bt3, 0, data, bt1.length + bt2.length, bt3.length);
            return data;
        }
    
    
        /**
         * Byte[] 与 int 互转
         * =============================================================================================
         */
    
        /**
         * 将int转为高字节在前,低字节在后的byte数组(大端)
         *
         * @param n int
         * @return byte[]
         */
        public static byte[] intToByteBig(int n) {
            byte[] b = new byte[4];
            b[3] = (byte) (n & 0xff);
            b[2] = (byte) (n >> 8 & 0xff);
            b[1] = (byte) (n >> 16 & 0xff);
            b[0] = (byte) (n >> 24 & 0xff);
            return b;
        }
    
        /**
         * 将int转为低字节在前,高字节在后的byte数组(小端)
         *
         * @param n int
         * @return byte[]
         */
        public static byte[] intToByteLittle(int n) {
            byte[] b = new byte[4];
            b[0] = (byte) (n & 0xff);
            b[1] = (byte) (n >> 8 & 0xff);
            b[2] = (byte) (n >> 16 & 0xff);
            b[3] = (byte) (n >> 24 & 0xff);
            return b;
        }
    
    
        /**
         * byte数组到int的转换(小端)
         *
         * @param bytes
         * @return
         */
        public static int bytes2IntLittle(byte[] bytes) {
            int int1 = bytes[0] & 0xff;
            int int2 = (bytes[1] & 0xff) << 8;
            int int3 = (bytes[2] & 0xff) << 16;
            int int4 = (bytes[3] & 0xff) << 24;
    
            return int1 | int2 | int3 | int4;
        }
    
        /**
         * byte数组到int的转换(大端)
         *
         * @param bytes
         * @return
         */
        public static int bytes2IntBig(byte[] bytes) {
            int int1 = bytes[3] & 0xff;
            int int2 = (bytes[2] & 0xff) << 8;
            int int3 = (bytes[1] & 0xff) << 16;
            int int4 = (bytes[0] & 0xff) << 24;
    
            return int1 | int2 | int3 | int4;
        }
    
    
        /**
         * Byte[] 与 short 互转
         * =============================================================================================
         */
    
        /**
         * 将short转为高字节在前,低字节在后的byte数组(大端)
         *
         * @param n short
         * @return byte[]
         */
        public static byte[] shortToByteBig(short n) {
            byte[] b = new byte[2];
            b[1] = (byte) (n & 0xff);
            b[0] = (byte) (n >> 8 & 0xff);
            return b;
        }
    
        /**
         * 将short转为低字节在前,高字节在后的byte数组(小端)
         *
         * @param n short
         * @return byte[]
         */
        public static byte[] shortToByteLittle(short n) {
            byte[] b = new byte[2];
            b[0] = (byte) (n & 0xff);
            b[1] = (byte) (n >> 8 & 0xff);
            return b;
        }
    
    
        /**
         * 读取小端byte数组为short
         *
         * @param b
         * @return
         */
        public static short byteToShortLittle(byte[] b) {
            return (short) (((b[1] << 8) | b[0] & 0xff));
        }
    
        /**
         * 读取大端byte数组为short
         *
         * @param b
         * @return
         */
        public static short byteToShortBig(byte[] b) {
            return (short) (((b[0] << 8) | b[1] & 0xff));
        }
    
        /**
         * Byte[] 与 long 互转
         * =============================================================================================
         */
    
        /**
         * long类型转byte[] (大端)
         *
         * @param n
         * @return
         */
        public static byte[] longToBytesBig(long n) {
            byte[] b = new byte[8];
            b[7] = (byte) (n & 0xff);
            b[6] = (byte) (n >> 8 & 0xff);
            b[5] = (byte) (n >> 16 & 0xff);
            b[4] = (byte) (n >> 24 & 0xff);
            b[3] = (byte) (n >> 32 & 0xff);
            b[2] = (byte) (n >> 40 & 0xff);
            b[1] = (byte) (n >> 48 & 0xff);
            b[0] = (byte) (n >> 56 & 0xff);
            return b;
        }
    
        /**
         * long类型转byte[] (小端)
         *
         * @param n
         * @return
         */
        public static byte[] longToBytesLittle(long n) {
            byte[] b = new byte[8];
            b[0] = (byte) (n & 0xff);
            b[1] = (byte) (n >> 8 & 0xff);
            b[2] = (byte) (n >> 16 & 0xff);
            b[3] = (byte) (n >> 24 & 0xff);
            b[4] = (byte) (n >> 32 & 0xff);
            b[5] = (byte) (n >> 40 & 0xff);
            b[6] = (byte) (n >> 48 & 0xff);
            b[7] = (byte) (n >> 56 & 0xff);
            return b;
        }
    
        /**
         * byte[]转long类型(小端)
         *
         * @param array
         * @return
         */
        public static long bytesToLongLittle(byte[] array) {
            return ((((long) array[0] & 0xff) << 0)
                    | (((long) array[1] & 0xff) << 8)
                    | (((long) array[2] & 0xff) << 16)
                    | (((long) array[3] & 0xff) << 24)
                    | (((long) array[4] & 0xff) << 32)
                    | (((long) array[5] & 0xff) << 40)
                    | (((long) array[6] & 0xff) << 48)
                    | (((long) array[7] & 0xff) << 56));
        }
    
        /**
         * byte[]转long类型(大端)
         *
         * @param array
         * @return
         */
        public static long bytesToLongBig(byte[] array) {
            return ((((long) array[0] & 0xff) << 56)
                    | (((long) array[1] & 0xff) << 48)
                    | (((long) array[2] & 0xff) << 40)
                    | (((long) array[3] & 0xff) << 32)
                    | (((long) array[4] & 0xff) << 24)
                    | (((long) array[5] & 0xff) << 16)
                    | (((long) array[6] & 0xff) << 8)
                    | (((long) array[7] & 0xff) << 0));
        }
    
    }
    
    
    展开全文
  • 前些时候面试的时候的考题,现场没有写好,回来仔细想了想写出来的。聊表慰藉吧。
  • 大端,小端,go

    2021-02-27 22:42:01
    什么是大端序和小端序? 如何快速区分大端序和小端序? 为什么会有大端序和小端序? go如何处理二进制序列化? 让我们来聊聊大端序小端序,以及go语言是如何处理的。主要是回答上面4个问题。 大端序和小端序的概念...
  • "大端" "小端"的来源关于大端小端名词的由来,网传有一个有趣的故事,可以追溯到1726年的Jonathan Swift的《格列佛游记》,其中一篇讲到有两个国家因为吃鸡蛋究竟是先打破较大的一端还是先打破较小的一端而争执不休...
  • C语言-- 大端小端详解

    2020-12-06 08:40:00
    一、什么是大端和小端所谓的大端模式,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。所谓的小端模式,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。简单来...
  • 1、大端和小端核心是什么? 大端模式和小端是实际的字节顺序和存储的地址顺序对应关系的两种模式,总结如下: 大端模式:低地址对应高字节 小端模式:低地址对应低字节 不管是大端还是小端模式,我们在读取和存储...
  • 昨晚帮导师从指令中恢复图像的时候,导师要我转换成raw格式,也就是记录图像像素的二进制序列,然后反复强调让我注意大端小端。当时我也没在意,用ofstream的write方法一个个地写进去,发现有部分数据存储顺序和其他...
  • 但是,大多数情况下,数据不是按照单字节的方式存储的,例如会有类似于int,double等数据类型,这就涉及到存储顺序的问题了,于是也就出现了两种存储方:大端模式(big endian)和小端模式(little endian)。
  • C语言-大端存储和小端存储

    热门讨论 2022-07-18 16:39:55
    C语言-大端存储和小端存储
  • 「内存分配」大端小端详解

    多人点赞 热门讨论 2021-07-09 14:04:21
    大端小端是计算机系统内存存储的两种模式,可谓是如雷贯耳的存在了。 我本以为自己已经是完全明白了,然而有时碰到的时候却发现概念有些混淆,一旦遇到复杂的场景就不知所措了。 所以写下此篇博客记录心路历程。 ...
  • 简单理解大端小端

    2021-07-14 11:17:19
    大端和小端是计算机写内存的习惯。就如人类写文章时的顺序一般,现代人从左往右,古人却是竖书成行(自上而下写满一行后,再自右向左换行。);有些计算机规定从左往右写内存,有些规定从右往左写内存。 大端就是大...
  • 何为大端、小端 大端还是小端不好理解和记忆是因为当初翻译是有问题的。翻译成“高尾端”和“低尾端”会更好理解。 如果把一个数看成一个字符串,比如11223344看成"11223344",末尾是个'\0','11'到'44'各占用一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 46,086
精华内容 18,434
关键字:

大端