大端序与小端序——字节序与位域的存储
谈到字节序的问题,必然牵涉到两大CPU派系——Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big-endian(大端序、大字节序、高字节序)方式存储数据,而x86系列则采用little-endian(小端序、小字节序、低字节序)方式存储数据。
何为大端序和小端序?
大端序:字数据的高字节存储在低地址中。
小端序:字数据的低字节存储在低地址中。
其实这两个概念不难理解,但是比较容易记混。由于大端序和小端序都是从存储器的低地址开始向高地址存储数据,不同的是一个字数据中先存高字节还是先存低字节。可以记住这样一个口诀:“高大低小”。“高”是指高字节,“大”是指大端序,“低”是指低字节,“小”是指小端序,“高大低小”也即先存高字节为大端序,先存低字节为小端序。
举个常见的例子。
位宽为32bit的CPU,要存储的字数据为0x1234abcd,起始地址为0x4000。
大端序:
地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
内容 | 0x12 | 0x34 | 0xab | 0xcd |
小端序:
地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
内容 | 0xcd | 0xab | 0x34 | 0x12 |
以上是字节域中,比较容易理解。
下面说一下在位域中。
先看一个例子。
(487条消息) 位域和大小端_icesongqiang的博客-CSDN博客_大小端 位域
//小端 0001 0010 0011 0100 0101 0110 0111 1000
typedef struct{
unsigned int first:7; //111 1000 从低位开始
unsigned int second:12; //100 0101 0110 0
unsigned int third:13; //0001 0010 0011 0
}HighLow;
//大端 0111 1000 0101 0110 0011 0100 0001 0010
typedef struct{
unsigned int third:13; //0001 0010 0011 0 从低位开始。
unsigned int second:12; //100 0101 0110 0
unsigned int first:7; //111 1000
}LowHigh;
综合起来,就是两句话。
高大低小:先存高字节为大端序,先存低字节为小端序。
高高低低:大端序先把高bit位存储在高位地址,小端序先把低bit位存储在低位地址。
最后附加一个常用的C程序来判断是大端序还是小端序。
网络是大端字节序
x86是小端字节序
如://192.168.5.243
大端字节序: F3 05 A8 C0
小端 内存顺序: c0 a8 05 f3
如int a = 128大端是 0 0 0 128 高(字节)前低(字节)后
小端 128 0 0 0 低前高后
java字节序、主机字节序和网络字节序扫盲贴_aitangyong的博客-CSDN博客
java中一个int型数据占用4个字节,假如有一个16进制的int数,int value = 0x01020304;采用不同的字节序,在内存中的存储情况见下图:
显然大字节序,是比较符合人类思维习惯的。
java实现htonl
public static int htonl(int d){
int rslt = 0;
byte [] bs1 = new byte[4];
ByteUtil.putInt(bs1, d, 0);
byte[] bs2 = ReversEndian(bs1, 4, false);
rslt = ByteUtil.getInt(bs2, 0);
return rslt;
}
Java字节序(不同语言中的网络数据传输时字节序列转换)_IT小智的博客-CSDN博客
/**
* 将int转为低字节在前,高字节在后的byte数组
* @param n int
* @return byte[] ntohl 小端字节序 低前高后
*/
public static byte[] toLH(int n) {
byte[] b = new byte[4];
b[0] = (byte) (n & 0xff);
b[1] = (byte) (n >> 8 & 0xff);
b[2] = (byte) (n >> 16 & 0xff);
b[3] = (byte) (n >> 24 & 0xff);
return b;
}
/**
* 将int转为高字节在前,低字节在后的byte数组
* @param n int
* @return byte[] htonl 大端字节序 高前低后
*/
public static byte[] toHH(int n) {
byte[] b = new byte[4];
b[3] = (byte) (n & 0xff);
b[2] = (byte) (n >> 8 & 0xff);
b[1] = (byte) (n >> 16 & 0xff);
b[0] = (byte) (n >> 24 & 0xff);
return b;
}
/**
字节序包括:大端序和小端序,为什么要这么麻烦还要分门别类呢?举个例子,255用二进制表达就是1111 1111,再加1就是1 0000 0000,多了一个1出来,显然我们需要再用额外的一个字节来存放这个1,但是这个1要存放在第一个字节还是第二个字节呢?这时候因为人们选择的不同,就出现了大端序和小端序的差异。
而所谓大字节序(big endian),便是指其“最高有效位(most significant byte)”落在低地址上的存储方式。例如像地址a写入0x0A0B0C0D之后,在内存中的数据便是:
而对于小字节序(little endian)来说就正好相反了,它把“最低有效位(least significant byte)”放在低地址上。例如:
对于我们常用的CPU架构,如Intel,AMD的CPU使用的都是小字节序,而例如Mac OS以前所使用的Power PC使用的便是大字节序(不过现在Mac OS也使用Intel的CPU了)。
-
256 use big endian:
-
int32 to bytes: [0 0 1 0]
-
bytes to int32: 256
-
256 use little endian:
-
int32 to bytes: [0 1 0 0]
-
bytes to int32: 256