谈谈字节序
walterxia walter.xia@gmail.com
说到字节序问题首先想到的是网络字节序(Network byte order)。不同主机之间需要通信,可能各个主机的体系结构不同,其本身存放字节的顺序是不同的,X86体系一般采用Little Endian(即低字节存放在低地址),而PowerPC相反,采用Big Endian(即高字节存放在低地址),而TCP/IP统一采用Big Endian,即所谓的网络字节序。这里的Endianness 即The attribute of a system that indicates whether integers arerepresented with the most significantbyte stored at the lowest address (big endian) or at the highest address(little endian). 其实Big Endian更加符合自然语言阅读的习惯,从左到右,从小到大。
注意的是,只有多字节的时候,才会考虑字节序的问题,如果是字符串,就没有这个问题了。对int、uint16、uint32等多于1字节类型的数据,就需要考虑了,如果是C/C++语言来操作的话,不同平台上的编译器会根据平台来产生不同字节序来存储这里的整形数值。注意的是JAVA语言编写的程序则唯一采用big endian方式来存储数据,有意思,是不是“Write once, run anywhere”那句名言所倡导的。
说到了跨平台,不同系统操作不同的文件,如何实现的?现流行的unicode,有UTF-8, UTF-16, UTF-32等。UTF-8以单字节为编码单位,所以不存在字节序。在UTF-16中,字节顺序记号BOM(Byte Order Mark)被放置为档案或字符串流的第一个字符,以标示在此档案或字符串流中,以所有十六位元为单位的字码的尾序(字节顺序)。如果十六位元单位被表示成大尾序,这字节顺序记号字符在序列中将呈现0xFE,其后跟着0xFF(其中的0x用来标示十六进制)。如果十六位元单位使用小尾序,这个字节序列为0xFF,其后接着0xFE。虽然字节顺序记号亦可以用于UTF-32,但这个编码很少用于传输,其规则如同UTF-16。对于已于IANA注册的字符集UTF-16BE、UTF-16LE、UTF-32BE和UTF-32LE等来说,不可使用字节顺序记号。文档开头的U+FEFF会被解释成一个(已舍弃的)"零宽度无断空白",因为这些字符集的名字已决定了其字节顺序。对于已注册字符集UTF-16和UTF-32来说,一个开头的U+FEFF则用来表示字节顺序。再比如,标签图像文件格式(Tagged Image File Format,简写为TIFF)是一种主要用来存储包括照片和艺术图在内的图像的文件格式。每个TIFF文件都是从指示字节序的两个字节开始的。“II”表示小端序、“MM”表示大端序。当然,流行的其他文档、图片、数据格式都会考虑字节序的问题。
如何写字节序独立的程序呢?在写应用程序的时候,尤其是网络程序的时候,下面的函数或宏htons/ntohs/htonl /ntohl是被使用的。Linux 内核定义了一套宏定义来处理之间的转换, 在处理器字节序和你需要以特定字节序存储和加载的数据之间. 例如:
u32 cpu_to_le32 (u32) / u32 le32_to_cpu(u32);
有一些编程的问题有关字节序:
(1) 如何实现上述所讲的转换函数或宏
(2) 写一段C程序来判断所在的机器是什么Endian呢?
(3) 如何将一个字符串形式的IP地址转换为一个以网络字节序存放的无符号整型值 unsigned int cip2uint(char cip[]),前提:设定开发的系统为LittleEndian的,而且不能使用系统函数实现。
参考:
http://unicode.org/faq/utf_bom.html
http://zh.wikipedia.org/wiki/TIFF
http://www.ibm.com/developerworks/aix/library/au-endianc/index.html?ca=drs-
http://en.wikipedia.org/wiki/Endianness
http://zh.wikipedia.org/wiki/%E5%AD%97%E8%8A%82%E5%BA%8F
http://www.gnu.org/software/libc/manual/html_node/Byte-Order.html
http://oss.org.cn/kernel-book/ldd3/ch11s04.html