这个章节主要介绍在无论是通过网络传输数据,还是内存中存储数据最重要的基础字节序。
使用C/C++进行网络编程的程序员,肯定会接触到“字节序”的概念。而Java程序员,因为JVM帮我们屏蔽了大量的底层细节和复杂性,导致我们对于这些底层的概念非常缺失。而字节序无论在网络编程或音视频文件处理中都是一个重要的概念。
譬如:从网络中传输的音频流,如果需要编码为WAV文件,则需要考虑从网络中传输的字节是何种字节序,而WAV文件又需要何种字节序。如不清楚的话,则最后生成的文件就是错误的。
1 字节序
字节顺序是指占用内存多于一个字节类型的数据在内存中的存放顺序,有小端、大端两种顺序。
- BIG-ENDIAN:大端,将高字节数据存放在低地址处,低字节数据存放在高地址处。
- LITTLE-ENDIAN:小端,将低字节数据存放在内存低地址处,高字节数据存放在内存高地址处。
注意:字节序是面向多字节类型定义的,比如2字节、4字节、8字节整型、长整型、浮点型等,单字节的字符串一般不用考虑。
1.1 示例
16进制数据0x1234的二进制表示为:0001 0010 0011 0100,占用2个字节,分别为十六进制的12和34。以该数据分别以大端和小端格式存储。
1.1.1 LITTLE-ENDIAN
在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 | 0x4000 | 0x4001 |
---|---|---|
存放内容 | 0x34 | 0x12 |
1.1.2 BIG-ENDIAN
而在Big-endian模式CPU内存中的存放方式则为:
内存地址 | 0x4000 | 0x4001 |
---|---|---|
存放内容 | 0x12 | 0x34 |
如果以不同的方式读取,则会发生错误。
2 主机字节序
至于计算机到底是BIG-ENDIAN、LITTLE-ENDIAN、跟CPU有关的,一种CPU不是BIG-ENDIAN就是LITTLE-ENDIAN。IA架构(Intel、AMD)的CPU中是Little-Endian,而PowerPC 、MIPS UNIX、HP-PA UNIX、SPARC和Motorola处理器是Big-Endian。
3 网络字节序
网络字节序是指数据在网络上传输时是大端还是小端的,在Internet的TCP/IP协议规定了一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用大端字节序。
4 Java字节序
JAVA字节序指的是在JAVA虚拟机中多字节类型数据的存放顺序,JAVA字节序也是大端字节序。
那么Jvm是怎么判断你的计算机是大端存储、还是小端存储呢?JDK为我们提供一个类ByteOrder,通过以下代码就可以知道机器的字节序
System.out.println(ByteOrder.nativeOrder());
在java.nio包下提供了ByteOrder、ByteBuffer等于字节序相关的类,我们也可以改变JVM中默认的字节序。
public class JVMEndianTest {
public static void main(String[] args) {
int x = 0x01020304;
ByteBuffer bb = ByteBuffer.wrap(new byte[4]);
bb.asIntBuffer().put(x);
String ss_before = Arrays.toString(bb.array());
System.out.println("默认字节序 " + bb.order().toString() + "," + " 内存数据 " + ss_before);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.asIntBuffer().put(x);
String ss_after = Arrays.toString(bb.array());
System.out.println("修改字节序 " + bb.order().toString() + "," + " 内存数据 " + ss_after);
}
5 Wav文件
Wav文件则是小端字节序。