底层工作者手册之C语言基础及项目开发2.4.4.pdf
(1.86 MB)
2.4.1 字节序大小端大端字节序对应的英文单词是big-endian,小端字节序对应的英文单词是little-endian。
endian这个单词在英文词典里一般查不到,这个单词出自Jonathan Swift写的讽刺小说《格列佛游记》,这本书中描述了一个小人国里总会发生一些意想不到的事情,有一次小人们因为是应该先从**蛋大的一端敲开还是小的一端敲开而引发了战争,支持从大端敲开的人被归为Big Endian,支持从小端敲开的人被归为Littile Endian。
在计算机世界里同样存在着这种“战争”,各个芯片厂商为了自身利益的需要提出了不同的两种字节序,从而引发了应该使用哪种字节序的争论。为了平息这场争论,Danny Cohen在其著名的论文"On Holy Wars and a Plea for Peace"中引入了big-endian和little-endian,分别对应着这两种字节序。这两种字节序共存的现实给软硬件设计带来了不小的麻烦,这种非常形象贴切的描述对这种情况真是一个不小的讽刺。
绝大部分处理器的最小存储单元是Byte,也就是说每个地址对应一个字节的数据,8位机时代对于处理器来说是不存在大小端问题的,它的机器指令只能以Byte为单位对存储数据进行访问,对多Bytes长度的数据类型进行访问也是由相对应多个的机器指令组成的,每条机器指令中规中矩的访问一个Byte的数据。但到16位机、32位机甚至64位机时代问题就来了,为了保持兼容性,这些处理器仍然是以Byte作为最小的存储单元,每个地址仍对应一个Byte数据,但为了提高性能,处理器增加了同时访问多字节的机器指令,以32位机为例,它的一条机器指令可以同时访问32bits的数据,也就是4地址的数据,对应4个字节。4个字节的数据作为一个整体表示一个数,那么它们在内存中是如何存放呢,这就涉及到大小端模式的问题了,看下图:
图 24
数值在内存中的存放模式
大端模式是将数的高位字节放在低位地址,将数的低位字节放在高位地址,而小端模式则正好反过来,是将数的高位字节放在高位地址,将数的低位字节放在低位地址,如上图所示。
我们再来看看上节例2.4.7中值为0x12345678的int型变量addr在Memory窗口的截图,如下图所示:
图 25
查看Memory内存窗口
其中最高位字节0x12被放在了最高地址0x2000020B,次高位字节0x34被放在了地址0x2000020A,次低位字节0x56被放在了地址0x20000209,最低位字节0x78被放在了地址0x20000208,按照这个存放顺序来看,我们所使用的处理器是小端模式的。
如果我们只使用变量而不直接访问内存是不会觉察到大小端模式存在的,比如说我们定义了一个int型的变量addr,在使用addr这个变量时它所占用的4字节内存空间是作为一个整体出现的,我们所能看到的只有变量addr,看不到它内部的4个字节,变量addr像一个黑盒一样罩住了它内部的4个字节的内存空间,我们访问addr时,处理器会按照大端或者小端的规则将这4个字节内存空间中的数据与变量addr的数值对应起来。对内存的访问分为读和写两种操作,下面我们仍以0x20000208~0x2000020B地址的变量addr为例来看看大小端模式的工作流程。
u 大端模式
将数0x12345678写入到变量addr中时,硬件就会将数0x12345678看作成4个字节0x12、0x34、0x56和0x78,分别存入0x20000208~0x2000020B这4个地址中,每个字节的数与地址按照大端模式的规则一一对应,这种对应关系是由硬件自动完成的。
图 26
大端模式
当读取变量addr数值时,硬件就会从0x20000208~0x2000020B这4个地址中读出4个字节,按照大端模式的对应规则将这4个地址中的4个字节恢复成0x12345678,这种对应规则也是由硬件自动完成的。
u 小端模式
从软件角度来看,小端模式的读写过程与大端模式的读写过程是一样的,写过程是将数0x12345678写入到变量addr中,读过程是从addr中取得数0x12345678,与大端模式不同之处在于硬件部分,硬件在执行读写时采用了数与地址不同的映射关系,这才是产生大小端模式的根本原因。
图 27
小端模式
硬件通过映射关系屏蔽了变量addr所对应的内存空间细节,因此软件访问变量addr根本就感觉不到大小端的存在,这就好比是我们把钱存到银行,我们不知道银行把这些钱放在哪里了,我们只需要知道我们还能从银行取出这些钱就行了。如果我们进入银行跟踪这些钱,也许会发现这些钱根本就没在银行里,可能已经被其他人取走了,在我们取钱时银行又拿了别人的钱给了我。进入银行就好比我们绕过了硬件的映射关系,直接去查看内存,大小端的问题就呈现在我们眼前了。
在上一节我们说过Memory窗口有多种查看方式,我们将查看方式更改为int类型,图25就会以int的类型显示数据,如下所示:
图 28
查看Memory内存窗口
可以看到变量addr在内存中的4个地址组合成一个整体显示出了addr的数值,与变量的值是一样的都是12345678,这样就方便我们查看内存数据了,不需要再考虑大小模式转换。不过这种方式是将内存中所有的数据都按照设置的类型显示,比如说当前显示类型为int型,那么几个char型、short型数也会被组合成int型来显示。
长度超过1个Byte的类型都存在大小端的问题,比如说2Bytes的short型、unsigned short型也需要按照大小端的规则在内存中存放。
有的处理器采用了小端模式,如PC机里的Intel X86 CPU,ARM等处理器,有的处理器则采用了大端模式,如PowerPC等处理器,还有一些处理器同时支持这2种模式,如TI的一些DSP处理器,可以选择使用其中的一种模式。
对于8bits的51单片机来说硬件每次只能读写一个Byte的数据,不存在大小端模式的概念,但它的C语言却是能支持16bits的int型变量,这就需要使用2个地址存放数据,这也会涉及到变量的存放规则,比如说它的一个16bits的int型变量值为0x1234,地址是0x40和0x41,其中0x12被存储在0x40地址,0x34被存储在0x41地址,从这点来看好像是大端模式,但这并不是我们前面介绍过的大端模式。原因在于51单片机对16bits数据读写时是使用了2条机器指令,每条机器指令会有一个地址与之对应,可以认为是重复了2次对8bits数据的操作组成了一次对16bits数据的操作,对于硬件来说,看不到16bits的数据,只能看到8bits的数据,数的高低位与地址的对应关系是由编译器决定的,而不是由硬件决定的。而对于本手册所使用的ARM处理器来说,对16bits数据读写时只使用1条机器指令,这条指令只对应一个地址,而一个地址只对应8bits数据,因此这就需要使用1个地址存储2个地址的数据,这就涉及到了大小端模式问题,数的高低位与地址的对应关系是由硬件决定的,编译器根本插不上手。
因此说51单片机是不存在大小端模式的,它所表现出的大小端是编译器的大小端,是编译器安排了数的存储模式,与处理器无关。
除了在数据存储时会体现大小端的概念,在数据传输时也会存在大小端的问题,数据传输是从一个处理器发送到另一个处理器的过程,尽管物理层中的数据不全是以Byte为单位进行传输的,但数据在收发两端处理器中仍然是以Byte为单位进行管理的,这就会涉及到大小端问题,比如说处理器A中有一个int型的变量addr,它的值为0x12345678,需要将这个值发送给处理器B,如果按照从数值高位到低位的顺序发送,那么发送出的数据为0x12->0x34->0x56->0x78,接收端处理器B也按同样的顺序接收到这4个Bytes的数据,但问题也来了,处理器B不知道处理器A发送的顺序,如果按照从高位到低位的顺序组合,那么接收到的数据就是0x12345678,如果按照从低位到高位的顺序组合,那么接收到的数据就是0x78563412,这就出错了,因此在网络传输中需要收发双方约定好发送的顺序。
论坛字数限制,请下载pdf文档查看 |