Byte Endian是指字节在内存中的组织, 所以也称它为Byte Ordering或Byte Order.
对于数据中跨越多个字节的对象, 我们必须为它建立这样的约定:
(1)它的地址是多少?
(2)它的字节在内存中是如何组织的?
针对第一个问题,有这样的解释:
对于跨越多个字节的对象,一般它所占的字节都是连续的,它的地址等于它所占字节最低地址. (链表可能是个例外,但链表的地址可看作链表头的地址).
比如:int x,它的地址为0x100. 那么它占据了内存中的Ox100,0x101,0x102,0x103这四个字节(32位系统,所以int占用4个字节).
上面只是内存字节组织的一种情况: 多字节对象在内存中的组织有一般有两种约定, 考虑一个W位的整数.
它的各位表达如下:[Xw-1,Xw-2,...,X1,X0]
MSB (Most Significant Byte,最高有效字节)为[Xw-1,Xw-2,...Xw-8];
LSB (Least Significant Byte,最低有效字节)为[X7,X6,...,X0]。
其余的字节位于MSB,LSB之间.
LSB和MSB谁位于内存的最低地址,即谁代表该对象的地址?
这就引出了Big Endian与Little Endian 的问题.
如果LSB在MSB前面,既LSB是低地址,则该机器是Little Endian, 反之则是Big Endian.
DEC(Digital EquipmentCorporation, 现在是Compaq公司的一部分)和Intel的机器(X86平台)一般采用Little Endian.
IBM, Motorola(PowerPC), Sun的机器一般采用Big Endian.
当然, 这不代表所有情况. 有的CPU即能工作于Little Endian, 又能工作于Big Endian, 比如ARM, Alpha, 摩托罗拉的PowerPC. 具体情形参考处理器手册.
具体这类CPU是Big Endian还是Little Endian, 应该和具体设置有关.
(如PowerPC支持little-endian字节序,但在默认配置时是big-endian字节序)
一般来说, 大部分用户的操作系统(如windows, FreeBsd, Linux)是Little Endian的.少部分, 如MacOS是Big Endian的.
所以说, Little Endian还是Big Endian与操作系统和芯片类型都有关系.
Linux系统中,你可以在/usr/include/中(包括子目录)查找字符串BYTE_ORDER(或_BYTE_ORDER,__BYTE_ORDER),确定其值,BYTE_ORDER中文称为字节序. 这个值一般在endian.h或machine/endian.h文件中可以找到, 有时在feature.h中, 不同的操作系统可能有所不同.
Big Endian是指低地址存放最高有效字节(MSB), 而Little Endian则是低地址存放最低有效字节(LSB).
用文字说明可能比较抽象, 下面用图像加以说明. 比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:
Big Endian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little Endian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
从上面两图可以看出, 采用Big Endian方式存储数据是符合我们人类的思维习惯的.
为什么要注意字节序的问题呢?你可能这么问. 当然, 如果你写的程序只在单机环境下面运行, 并且不和别人的程序打交道, 那么你完全可以忽略字节序的存在.但是, 如果你的程序要跟别人的程序产生交互呢?在这里我想说说两种语言. C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的, 而Java编写的程序则唯一采用Big Endian方式来存储数据. 试想, 如果你用C/C++语言在x86平台下编写的程序跟别人的Java程序互通时会产生什么结果?就拿上面的0x12345678来说, 你的程序传递给别人的一个数据, 将指向0x12345678的指针传给了Java程序,由于Java采取Big Endian方式存储数据, 很自然的它会将你的数据翻译为0x78563412. 什么? 竟然变成另外一个数字了? 是的, 就是这种后果. 因此, 在你的C程序传给Java程序之前有必要进行字节序的转换工作.
无独有偶,所有网络协议也都是采用Big Endian的方式来传输数据的. 所以有时我们也会把Big Endian方式称之为网络字节序. 当两台采用不同字节序的主机通信时, 在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输.
可是有朋友仍然会问, CPU存储一个字节的数据时其字节内的8个比特之间的顺序是否也有Big Endian和Little Endian之分?或者说是否有比特序的不同?
实际上, 这个比特序是同样存在的. 下面以数字0xB4(10110100)用图加以说明.
Big Endian
MSB LSB
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little Endian
LSB MSB
---------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
实际上, 由于CPU存储数据操作的最小单位是一个字节, 其内部的比特序是什么样对我们的程序来说是一个黑盒子. 也就是说, 你给我一个指向0xB4这个数的指针,对于Big Endian方式的CPU来说, 它是从左往右依次读取这个数的8个比特; 而对于Little Endian方式的CPU来说, 则正好相反, 是从右往左依次读取这个数的8个比特. 而我们的程序通过这个指针访问后得到的数就是0xB4,字节内部的比特序对于程序来说是不可见的, 其实这点对于单机上的字节序来说也是一样的.
那可能有人又会问,如果是网络传输呢?会不会出问题?是不是也要通过什么函数转换一下比特序?嗯,这个问题提得很好.假设Little Endian方式的CPU要传给Big Endian方式CPU一个字节的话, 其本身在传输之前会在本地就读出这个8比特的数, 然后再按照网络字节序的顺序来传输这8个比特, 这样的话到了接收端不会出现任何问题. 而假如要传输一个32比特的数的话, 由于这个数在Little Endian方存储时占了4个字节, 而网络传输是以字节为单位进行的, Little Endian方的CPU读出第一个字节后发送, 实际上这个字节是原数的LSB, 到了接收方反倒成了MSB从而发生混乱.
用函数判断系统是Big Endian, 还是 Little Endian
//如果字节序为Big Endian, 返回true
//如果字节序为Little Endian, 返回false
bool IsBig_Endian()
{
unsigned short test = 0x1122;
if(*((unsignedchar) & test) == 0x11)
return true;
else
return false;
}