【字符编码系列之一】多字节数据的字节序

字符和字节的区别

  • 字节(byte):是一个8bit的存储单元,取值范围是0x00~0xFF。

  • 字符(character):为语言意义上的一个符号,范围不一定。一个字符占用的字节数,随着编码方式的不同而不同。可能是一个字节,也可能是多个。对于大于一个字节的字符,我们称之为多字节数据。


大端对齐 (Big Endian) 和小端对齐 (Little Endian) 

一个字符可能占用多个字节,那么这多个字节在计算机中如何存储呢?
比如字符0xABCD:

//小端对齐(Little Endian)
addr:0x2c2d98	CD
addr:0x2c2d99	AB

//大端对齐(Big Endian)
addr:0x2c2d98	AB
addr:0x2c2d99	CD
大端对齐(Big Endian):低地址存放 多字节数据 的高字节。
小端对齐(Little Endian):低地址存放 多字节数据 的低字节。

这就是字节序的问题。为什么要注意字节序的问题呢?当然,如果你写的程序只在单机环境下面运行,不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互。那么对于以下数据:

addr:0x2c2d98	AB
addr:0x2c2d99	CD
小端对齐方式读出的就是0xCDAB,而大端对齐方式上读出的就是0xABCD.这就是对于同一结构的不同解释导致的通讯误解。

注意,这里所说的大端对齐与小端对齐,指的是对于一个多字节数据内部,而不是不同的数据之间 怎么存储。

举个例子,我们假定int类型占用4个字节

int a = 0x01020304, b = 0x05060708;

那么不管是大端对齐还是小端对齐,是指的变量a内部是怎么表示的,而不是指变量a与变量b之间的先后关系。

//小端对齐(Little Endian)
//变量a
addr:0x2c2d92	04
addr:0x2c2d93	03
addr:0x2c2d94	02
addr:0x2c2d95	01

//变量b
addr:0x2c2d96	08
addr:0x2c2d97	07
addr:0x2c2d98	06
addr:0x2c2d99	05

//大端对齐(Big Endian)
//变量a
addr:0x2c2d92	01
addr:0x2c2d93	02
addr:0x2c2d94	03
addr:0x2c2d95	04

//变量b
addr:0x2c2d96	05
addr:0x2c2d97	06
addr:0x2c2d98	07
addr:0x2c2d99	08

我们看到,大小端对齐,指的是变量a的内部怎么存储,而不是指变量a与变量b之间是怎么存储的。

下面这张图生动的阐释了刚讲过的内容。

【字符编码系列之一】多字节数据的字节序_第1张图片

所有网络协议也都是采用大端对齐(Big Endian)的方式来传输数据的。所以有时我们也会把Big Endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。 


判断一台机器是大端对齐还是小端对齐,可以通过下面这个小程序:

long iNum = 1;	//long类型肯定至少是2个字节
if(1 == *(char *)(&iNum))   //取iNum地址并强制转换为char*类型再取值,此时取到的值是低位地址上的值 
    std::cout << "Little Endian" << std::endl; 
else 
    std::cout << "Big Endian" << std::endl; 


BOM

在Unicode编码中有一个叫做 "Zero Width No-Break Space" ,中文译名作“零宽无间断间隔”的字符,它的编码是U+FEFF,也就是十六进制的0xFEFF。字符U+FEFF如果出现在字节流的开头,则用来标识该字节流的字节序,用来表示到底是高位在前还是低位在前。如果它出现在字节流的中间,则表达零宽度非换行空格的意义,用户看起来就是一个空格。从Unicode3.2开始,U+FEFF只能出现在字节流的开头,只能用于标识字节序。所以U+FEFF又有一个名字,那就是字节顺序标记(Byte Order Mark),简称BOM。现在U+FEFF只用作BOM,除此以外的用法已被舍弃。取而代之的是,使用U+2060来表达零宽度无断空白。

为什么U+FEFF可以用作BOM呢?那是因为由于Unicode中没有定义U+FFFE,而只定义了U+FEFF,因此只要出现 FF FE 或者 FE FF 这样的字节序列,就可以认为它是U+FEFF,并且可以判断出是Big Endian还是Little Endian了。


结语

  • 大端对齐(Big Endian)、小端对齐(Little Endian)是跟CPU有关的,每一种CPU只能是两者之一。IA架构(Intel Architecture)的CPU中是小端对齐的,而PowerPC 、SPARC和Motorola处理器则是大端对齐的。这其实就是所谓的主机字节序。
  • 网络字节序是指数据在网络上传输时是大端对齐还是小端对齐的。在Internet的网络字节序是大端对齐(Big Endian)。
  • 所谓的JAVA字节序指的是在JAVA虚拟机中多字节类型数据的存放顺序,JAVA字节序也是大端对齐(Big Endian)。
  • 在用C/C++写通信程序时,在发送数据前务必把多字节数据,从主机字节序转换到网络字节序,而接收数据后对多字节数据也必须从网络字节序转换到主机字节序。
  • 如果通信的一方是JAVA程序、一方是C/C++程序时,则需要在C/C++一侧使用以上几个方法进行字节序的转换,而JAVA一侧,则不需要做任何处理,因为JAVA字节序与网络字节序都是大端对齐(Big Endian),只要C/C++一侧能正确进行转换即可(发送前从主机序到网络序,接收时反变换)。如果通信的双方都是JAVA,则不用考虑字节序的问题了。 

你可能感兴趣的:(字符编码)