应该说没做底层开发(硬件或驱动)的人很可能不会彻底理解大小端的概念,大小端不是简单的一句“大端在前”还是“小端在前”能够概括的问题。在cpu, 内存, 操作系统, 编译选项, 文件,网络传输中均有大小端的概念,这些东西加在一起,就很容易把人搞晕。我自己就晕过很久。
为方便说明,再做一些定义:
(1) 内存
可以存储若干个单元数据的物理设备,每个单元存储1个字节,每个单元有一个地址,其地址线程增长。为方便说明,假设内存地址从 0000:0000 一直增加到FFFF:FFFF。
用一个带箭头的直线表示地址的增长方向,例如
-------------------------->
表示左边的数据处于低地址,右边的数据处于高地址。
(2) U32整型
对于unsigned int的变量,计算机是以32bits存储的,即连续的4个字节。比如说,对一个值为0x11223344的整数,在内存中的排列方式可能为:
-------------------------->
11
也可能为
44
从CPU说起
-------------------------->
11
程序
文件
程序的编译
从上面可以看出,cpu、程序、编译过程这一套东西,其大小端都必须是一致的。这里就简称为系统的大小端。
网络传输
在代码判断大小端
inline bool IsLittleEndian()
{
}
系统无关的写法
在传送和保存数据时,可以写一份不管系统是大端还是小端、结果都相同的代码。这是用移位来实现的。比如,我们从网络上接收到4个字节,想把它转成一个U32。
U8 buf[4]; // 接收到4个字节, 按大端传递
U32 v = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
一种错误的写法
在做编解码的时候会经常遇到关于大小端的问题,而写错的人大有人在,协议定义不好的也大有人在。一般来说,只要发现一个协议把数据定义成小端,那么我一般猜到了作者想干什么了。例如, 定义一个协议,发送以下数据,以小端发送。
U8 a
U8 b
U16 c
U32 d
那么程序基本上会这么写代码:
struct Msg
{
};
U8 buf[8];
Msg msg;
memcpy(&msg, buf, 8);
那intel的机器上,会发现这样刚好是对的。但是我要说这是不正确的写法。
BIT有大小端吗?
在软件编程领域,大小端总是按BYTE计算的,永远无需考虑BIT的大小端。因为数据的最小单元是BYTE,在物理环节中其顺序都固定好了,永远对的上。正因为如此,我们可以用位域来接收按位定义的信息,而不必担心大小端。如在PES的头部:
struct Flags
{
}
但要注意的是,这仅限于字节之内的情形。