精华内容
下载资源
问答
  • 小端
    千次阅读
    2022-03-10 19:47:26

    大端和小端

    起源

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

    什么是大端和小端

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

    1)大端模式:Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

    低地址 --------------------> 高地址
    0x12 | 0x34 | 0x56 | 0x78

    2)小端模式:Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

    低地址 --------------------> 高地址
    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字节的存储方式一样。
    大端模式 :符号位的判定固定为第一个字节,容易判断正负。

    为什么会有大小端模式之分呢?

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

    如何判断机器的字节序

    通过JDK的NIO包中的Bits来说明:

    private static final ByteOrder byteOrder;
    
    static ByteOrder byteOrder() {
    	if (byteOrder == null)
    		throw new Error("Unknown byte order");
    	return byteOrder;
    }
    
    static {
    	long a = unsafe.allocateMemory(8);
    	try {
    		unsafe.putLong(a, 0x0102030405060708L);
    		byte b = unsafe.getByte(a);
    		switch (b) {
    		case 0x01: byteOrder = ByteOrder.BIG_ENDIAN;     break;
    		case 0x08: byteOrder = ByteOrder.LITTLE_ENDIAN;  break;
    		default:
    			assert false;
    			byteOrder = null;
    		}
    	} finally {
    		unsafe.freeMemory(a);
    	}
    }
    

    常见的字节序

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

    • 常见CPU的字节序

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

    • 常见文件的字节序

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

    浮点数

    十进制转二进制

    如何将十进制转换成二进制浮点数呢, 先介绍一下十进制的浮点数 转换二进制的浮点数,分为两部分:

    先将整数部分转换为二进制,
    将小数部分转换为二进制, 然后将整数部分与小数部分相加。
    以 20.3 转换为例,

    20转换后变为 10100

    0.3 要转换二进制,需要乘2, 乘完之后 取整数部分,然后用乘的结果减去整数部分, 然后 接着乘2, 直至最后没有小数或者小数出现循环, 即乘完.

    20 = 10100 (二进制)

    0.3 * 2= 0.6 (0)

    0.6 * 2 = 1.2 (1)

    0.2 * 2= 0.4 (0)

    0.4 * 2 = 0.8 (0)

    0.8 *2 = 1.6 (1)

    计算到这里, 将再出现0.6,进入循环了,所以,结果

    0.3 = 0.010011001…1001

    所以20.3 = 10100.010011001…1001 (二进制).

    2.二进制数转化为科学计数法

    20.3 = 10100.010011001…1001 (二进制)=1.01000100110011E10……(十进制科学计数)=1.01000100110011E100……(二进制科学计数)

    这里使用移位存储,对于float来说,指数位加上127,double位加上1023(这里指的是存储,在比较的时候要分别减去127和1023)

    移位存储

    移位存储本质上是为了保证+0和-0的一致性。

    以float指数部分的这8位来分析,

    那么这8位组成的新的字节,我们来用下面的一串数字表示:0000 0000

    首先,我们假设不使用移位存储技术,而是单单看看这个 8位组成的新字节,到底能表示多少个数: 0000 0000 -1111 1111 即0-255,一共256个数。

    但是我们知道这8位数既要表示正数也要表示负数。

    所以将左边第一位拿出来表示正负的符号:

    第一个区间:

    0 000 0000-0 111 1111 
    即+0 到127
    

    这就是问题的所在:怎么会有两个0,一个正零,一个负零。

    这时候使用移位存储:float使用127(0011 1111)

    • 表示0 0+127=127 即 0000 0000 +0111 1111=0111 1111
    • 表示1 1+127=128 即 0000 0001 +0111 1111=0111 1111
    • 表示128 128+127=255 即 1000 0000+0111 1111=1111 1111

    最大的正数,再大就要溢出了。

    • 表示-1 -1+127=126=127-1 即 0111 1111-0000 0001=0111 1110
    • 表示-1 -2+127=125=127-2 即 0111 1111-0000 0010=0111 1101
    • 表示-127 -127+127=0 即0111 1111-0111 1111=0000 0000

    最小的负数,再小就溢出了。

    本文到此结束,感谢阅读。

    更多相关内容
  • 详解大端模式和小端模式

    千次阅读 2021-06-04 04:12:41
    如果从实际应用的角度说,采用小端模式的处理器需要在软件中处理端模式的转换,因为采用小端模式的处理器在与小端外设互连时,不需要任何转换。而采用大端模式的处理器需要在硬件设计时处理端模式的转换。大端模式...

    七、如何进行转换

    对于字数据(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-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]),之后再进行发送才可以得到正确的数据。

    展开全文
  • 总述:字节间字节内大端低地址的数据放在高字节位域:先从低地址对应数据字节的高bit开始分配小端低地址的数据放在低字节位域:先从低地址对应数据字节的低bit开始分配注释:对于大小端的几个结论:1.char型变量和...

    总述:

    字节间

    字节内

    大端

    低地址的数据放在高字节

    位域:先从低地址对应数据字节的高bit开始分配

    小端

    低地址的数据放在低字节

    位域:先从低地址对应数据字节的低bit开始分配

    注释:

    对于大小端的几个结论:

    1.char型变量和char型数组没有大小端的区分。

    2.需要转化数据类型大于1个字节的数据类型:short,int,枚举,联合体等。

    一.为什么要进行大小端转化?

    简单说一下为什么要进行大小端转化:目前我接触到的,arm是小端,dsp是大端,电脑是小段,网络数据一般为大端,当arm需要和dsp通信的时候就会存在大小端转化的问题,每种芯片为什么选用大小端的由来就不赘述了。

    二、字节间的大小端转化:

    1,举例说明字节间的大小端:

    内存中有如下一段数据(unsigned int 型),不同的大小端对这个段数据的理解不同,所以在我们实际工作中,当你查看到内存中的数据时候,首先要清楚这个处理器的大小端,才能理解这段数实际代表的值。

    内存地址

    0x00004000

    0x00004001

    0x00004002

    0x00004003

    0X12

    0x34

    0x56

    0X78

    如果是大端处理器:这段数代表:0x12345678;

    如果是小段处理器:这段数代表:0x78563412 ;

    内存中有如下一段数据(unsigned short 型):

    内存地址

    0x00004000

    0x00004001

    0X12

    0x34

    如果是大端处理器:这段数代表的值:0x1234;

    如果是小段处理器:这段数代表的值:0x3412 ;

    在两个不同大小端的处理器之间数据传输,数据再内存的存放顺序并没有变,不同大小端需要解读出通用的值,就需要进行大小端转化。

    2,C语言大小端转化调用库函数:

    htonl

    host to network ,l代表unsigned long型 也可以看成unsigned int

    htons

    host to network ,s代表unsigned short型

    ntohl

    network to host ,l代表unsigned long型 也可以看成unsigned int

    ntohs

    network to host ,s代表unsigned short型

    在网络传输中,一般要求是大端,而inter处理器是小端,network to host理解为大端转小端,而host to network 理解为小端转大端,本质上大端小端的转化算法是一致的,没有区别,

    3.代码验证:

    /*

    *程序说明:大小端字节间转化的一个案例

    *作者 :通信小卒

    *时间 :2019.8.25

    */

    #include#include#pragma comment(lib,"ws2_32.lib")

    int main(void)

    {

    unsigned int ultest=0x12345678;

    unsigned short ustest=0x1234;

    printf("ultest小端:%x\n",ultest);

    printf("ustest小端:%x\n",ustest);

    ultest=htonl(ultest);/*unsigned int 大小端转化*/

    ustest=htons(ustest);/*unsigned short 大小端转化*/

    printf("ultest大端:%x\n",ultest);

    printf("ustest大端:%x\n",ustest);

    while(1);

    }

    8ec3eae1d63d05699eef3c31933949b4.png

    三.对于字节内部位域大小端的理解与处理:

    1.位域处理方法(特别注意):

    大小端不同的芯片传递同一个结构体时,

    第一步:需要将结构体中的位域部分根据大小端判断进行翻转(int型按照凑32bits翻转,short型凑16bits翻转 ,char型凑8bits翻转)。

    第二步:接收到数据后,如果位域的数据类型是unsigned int型,就拼凑成32bits按照unsigned int进行大小端转化,如果是unsigned short 型就拼凑16bit按照unsigned short 进行大小端转化,如果是unsigned char型就不翻转。

    2.案例说明位域的存储方式:

    /*

    *程序说明:位域大小端字节转化的一个案例

    *作者 :通信小卒

    *时间 :2019.8.25

    */

    #include#include#define LOCAL_ENDIAN 1 /* 0为大端 1 为小端,根据处理器的类型赋值*/

    typedef struct struct_test

    {

    #ifdef LOCAL_ENDIAN

    unsigned int ula :7;

    unsigned int ulb :2;

    unsigned int ulc :5;

    unsigned int uld :3;

    unsigned int ule :6;

    unsigned int ulf :4;

    unsigned int ulg :5;

    #else

    unsigned int ulg :5;

    unsigned int ulf :4;

    unsigned int ule :6;

    unsigned int uld :3;

    unsigned int ulc :5;

    unsigned int ulb :2;

    unsigned int ula :7;

    #endif

    }struct_test_bit;

    int main(void)

    {

    struct_test_bit struct_test_endian;

    memset(&struct_test_endian,0,sizeof(struct_test_bit));

    struct_test_endian.ula=0x57;

    struct_test_endian.ulb=0x3;

    struct_test_endian.ulc=0x1c;

    struct_test_endian.uld=0x6;

    struct_test_endian.ule=0x2d;

    struct_test_endian.ulf=0x9;

    struct_test_endian.ulg=0x1c;

    printf("&struct_test_endian=%x\n",&struct_test_endian);

    while(1);

    }

    处理LOCAL_ENDIAN 不同,其他代码完全相同,在大小端不同的处理器上运行此代码,分析内存情况:

    2.1在小端处理器下看内存(#define LOCAL_ENDIAN 1 ):

    地址

    0X004AFC14

    0X004AFC15

    0X004AFC16

    0X004AFC17

    数据十六进制

    D7

    B9

    DB

    E4

    数据二进制

    11010111

    10111001

    11011011

    11100100

    跨字节的部分需要特别注意:

    在这里插入图片描述

    小端数据为:0x E4BDB9D7, 现将数据进行翻转看,然后从底地址数据开始读:

    b29c36336e248c649d23695a81d88379.png

    可以看出小端是从底地址的字节的底bit位开始分配。

    2.2在大端处理器运行看内存(#define LOCAL_ENDIAN 0):

    地址

    0X004AFC14

    0X004AFC15

    0X004AFC16

    0X004AFC17

    数据十六进制

    E4

    DB

    B9

    D7

    数据二进制

    11100100

    11011011

    10111001

    11010111

    跨字节的部分需要特别注意:

    f68b5f994489905f453065c4ceba7552.png

    三.对于枚举类型与联合体类型的处理。

    1.对于枚举类型按照unsigned int进行大小端转化就可以。

    2.对于联合体按照联合体中长度最长的数据类型以及长度进行转化就行。

    展开全文
  • "大端" "小端"的来源关于大端小端名词的由来,网传有一个有趣的故事,可以追溯到1726年的Jonathan Swift的《格列佛游记》,其中一篇讲到有两个国家因为吃鸡蛋究竟是先打破较大的一端还是先打破较小的一端而争执不休...
  • 但是,大多数情况下,数据不是按照单字节的方式存储的,例如会有类似于int,double等数据类型,这就涉及到存储顺序的问题了,于是也就出现了两种存储方:大端模式(big endian)和小端模式(little endian)。
  • 大小端也可以理解为字节顺序,或者端序、尾序,就是大端序(Big-Endian)、小端序(Little-Endian)。 1.大端模式:是指数据的高字节保存在内存的低地址中; 2.小端模式:是指数据的高字节保存在内存的高地址中; ...
  • 重要的话说三遍 大端模式和小端模式 大端模式和小端是实际的字节顺序和存储的地址顺序对应关系的两种模式。 大端模式:高位字节存放在低地址中,低位字节存放在高地址中。最直观的字节序。 小端模式:高位字节存放在...
  • 什么是大小?如何确定大小

    万次阅读 多人点赞 2019-08-25 19:59:40
    对于一个由2个字节组成的16位整数,在内存中存储这两个字节有两种方法:一种是将低序字节存储在起始地址,这称为小端(little-endian)字节序;另一种方法是将高序字节存储在起始地址,这称为大端(big-endian)字节序。...
  • 大小详解(判断+转换)

    万次阅读 多人点赞 2019-04-25 00:14:46
    一、什么是大端和小端 所谓的大端模式,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 所谓的小端模式,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 简单来说:大端...
  • 大端和小端传输字节序

    千次阅读 2020-08-18 14:23:03
    大端和小端 在计算机中是以字节为单位,每一个地址对应一个字节,一个字节8bit。在C中,除了8bit的char以外,还有16bit的short,32位的int,64位long,当然具体要由编译器决定,可以通过sizeof来获取不同类型在内存...
  • 数据存储:大端和小端

    千次阅读 2020-07-20 17:41:40
    大端和小端 大端小端测试 写入测试 字节码文件的大小端 前面说到,像C/C++这类语言编写的程序,它们被编译后,直接转换成了对应平台上的可被CPU直接运行的机器指令,转换之后,原本语言中的数据结构,例如...
  • 小端模式Little-Endian:低字节存于内存低地址,高字节存于内存高地址。 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。 首先我们来看下数据在大小端两...
  • 大小

    千次阅读 2017-04-09 11:55:34
    高字节对应高地址(大端模式)、高字节对应低地址(小端模式) union myUnion { int a; char b; } int is_little_endian(void) //用共用体的方法写大小端检测 //如果是小端模式就输出1,如果是大端模式就输出0...
  • 数据的存储,大端存储和小端小端存储

    千次阅读 多人点赞 2021-07-27 16:05:35
    在数据的存储中,有两种存储形式,大端存储和小端存储。 所有的数据都是在内存当中存的,而内存当中的基本单位是字节。 数据访存的基本单位是字节的话,那么一个整形变量(int)就有四个字节,那么就意味着数据在...
  • 查看当前系统是大端还是小端:sys.byteorder,Windows是小端的,而小端在我们看来是和常规反向地字节串的赋值表达式bytes((1,2,ord('1'),ord('2'))) # 可迭代类型,元素是数字Out[226]: b'\x01\x0212'bytes((0xff,))...
  • 大端模式和小端模式

    万次阅读 多人点赞 2018-08-28 17:09:25
    小端模式(Little-endian):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端,即逆序排列,低尾端; 例(无论是小端模式还是大端模式。每个字节内部都是按顺序排列): 1)大端模式: 低地址 ------...
  • PLC中的大端小端

    千次阅读 2019-11-20 08:37:10
    相信大家在阅读有关通讯数据传输、PLC数据存储等技术文档时,经常会碰到“Big-Endian”(大端对齐)与Little-Endian(小端对齐)术语。很多朋友不理解大端和小端模式,本文给大家写一下此知识点。 为什么有大端...
  • 大小及转换(C++)

    千次阅读 2022-03-03 17:32:32
    小端模式:数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。 假如在一...
  • 大端存储和小端存储

    千次阅读 2022-04-24 15:11:52
    二、 小端存储 低字节数据存储在低地址 注意:无论是大端存储还是小端存储,读取到的数据为0x0102030405 三、大端存储或小端存储都是由系统设定的,其二者区别在于低地址存储的数据,因此可以写程序进行判断。 #...
  • 数据通信--大小转换

    千次阅读 2020-11-08 15:02:25
    小端模式存放内容 大端模式存放内容 0x0000 0x78 0x12 0x0001 0x56 0x34 0x0002 0x34 0x56 0x0003 0x12 0x78 以0x12345678为例,可以看出。 大端,存放规则:12345678 ...
  • 00. 目录 文章目录00. 目录01. 概述02. 示例分析03....小端模式(Little-endian):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端,即逆序排列,低尾端; 例(无论是小端模式还是...
  • 大端模式与小端模式

    千次阅读 2021-12-08 15:29:54
    小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,数据从低位往高位放;这和我们的阅读习惯一致。 ...
  • x86、ARM分属大小

    千次阅读 2020-04-28 23:25:59
    小端模式:一个数据的高位在大的地址端,低位在小的地址端,x86也就是pc机就是小端的: #include"stdio.h" #include"stdlib.h" int main() { int a = 0x3736;//"7 6 " printf("%c\n",*(char*)&a); printf("%c\n",*(...
  • 大端小端存储方式详解

    千次阅读 2022-03-30 17:23:40
    小端存放:用存储器的低字节地址单元来存放数据的最高字节 注: 在存储器中,左边的字节地址单元低于右边的字节地址单元,字节地址单元一般由八位二进制组成;而在数据的表示中,左边的字节高于右边的字节(数据...
  • 大小以及对齐方式

    千次阅读 2018-08-16 15:34:02
    一、大小 计算机中,数据寻址时用的是低位字节的地址。即: C中的数据类型都是从内存的低地址向高地址扩展,取址运算"&amp;"都是取低地址。所以就有高位地址和低位地址区别,一般来说数字大的就是高...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,654,320
精华内容 661,728
关键字:

小端

友情链接: 2.rar