谈到字节顺序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用bigendian方式存储数据,而x86系列则采用littleendian方式存储数据。那么究竟什么是bigendian,什么又是littleendian呢?
其实big endian是指低地址存放最高有效字节(MSB),而littleendian则是低地址存放最低有效字节(LSB)。
用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:
Big Endian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
LittleEndian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
从上面两图可以看出,采用bigendian方式存储数据是符合我们人类的思维习惯的。而littleendian,!@#$%^&*,见鬼去吧-_-|||
为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用bigendian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取bigendian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。
无独有偶,所有网络协议也都是采用bigendian的方式来传输数据的。所以有时我们也会把bigendian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSIC中提供了下面四个转换字节序的宏。
big endian:最高字节在地址最低位,最低字节在地址最高位,依次排列。
little endian:最低字节在最低位,最高字节在最高位,反序排列。
一个例子:
如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x34 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
处理字符串的时候,涉及编码问题。我们常用的unicode的UCS2中就有big-endian和little-endian区分。此编码方式规定了字符串在内存的字节序。因此,在C++代码和Java代码处理字符串的时候,对于已经规定了字节序的字符串就不用处理了,只要都按照字符串的字节序解析就可以正确处理字符串。
ANSI规定的编码,如GB2312字符集的DBCS编码中,GB内码的存储格式始终是bigendian,即高位在前。还有UTF-8编码,根据遇到第一个字节的1的个数可以判断后面是否有字节属于当前字符编码,也解决了字节序问题。
因此,对于字符串的处理,具体字节序由编码已经规定了,不同编码字节序有所不同,使用的时候只要根据字节序编码规则就可以正确的处理字符串了。