一、什么是字节序
字节序,顾名思义就是字节的顺序。更具体的讲,它是多字节数据存储和传输时的字节顺序。
二、为什么有“字节序”这个东西?
计算机系统中内存是以字节为单位进行编址的,每个地址单元都唯一的对应着1个字节(8 bit)。
这可以应对char类型数据的存储要求,因为char类型长度刚好是1个字节,但是有些类型的长度是超过1个字节的(字符串虽然是多字节的,但它本质是由一个个char类型组成的类似数组的结构而已),比如C/C++中,short类型一般是2个字节,int类型一般4个字节等。
因此这里就存在着一个如何安排多个字节数据中各字节存放顺序的问题。正是因为不同的安排顺序导致了大端存储模式和小端存储模式的存在。
三、举个例子
十六进制数 0x12345678 共占4个字节,分别是0x12、0x34、0x56、0x78,因此在该数字中0x12属于高位数据,0x78属于地位数据。
注:
可以把内存看成是一个很大的数组,4G内存则是一个长度为4294967296的数组。
该数组的索引其实就是内存地址,左边是比较小的地址,右边则越来越大,直至最大值。
第一种顺序:高位字节排放在内存的低地址端,低位字节排放在内存的高地址端,叫大端模式
第二种顺序:低位字节排放在内存的低地址端,高位字节排放在内存的高地址端,叫小端模式,记忆法:“小端低低”
两者,大端模式比较符合人类的阅读习惯;小端模式更符合计算机的处理方式,因为计算机从低位开始处理。
四、模式特定
为什么截然相反的大小端存储模式能够并存至今?在标准化备受推崇的今天,为什么大小端谁都没有被另外一个所同化?我想这除了历史的惯性使然,还与它们各自的优缺点有关。
大端模式优点:
符号位在所表示的数据的内存的第一个字节中,便于快速判断数据的正负和大小
小端模式优点:
1、内存的低地址处存放低字节,所以在强制转换数据时不需要调整字节的内容;
(注解:比如把int的4字节强制转换成short的2字节时,就直接把int数据存储的前两个字节给short就行,因为其前两个字节刚好就是最低的两个字节,符合转换逻辑)
2、CPU做数值运算时从内存中依顺序依次从低位到高位取数据进行运算,直到最后刷新最高位的符号位,这样的运算方式会更高效
其各自的优点就是对方的缺点,正因为两者彼此不分伯仲,再加上一些硬件厂商的坚持(见1.3节),因此在多字节存储顺序上始终没有一个统一的标准
四、大小端的应用场景。
Intel的80×86系列芯片使用小端存储模式
ARM芯片默认采用小端,但可以切换为大端
MIPS芯片采用大端,但可以在大小端之间切换
在网络上传输的数据普遍采用的都是大端
Java 默认采用大端,可以切换大小端切换
五、如何判断主机序
方法一:通过将多字节数据强制类型转换成单字节数据,再通过判断起始存储位置是数据高字节还是低字节进行检测
#include<stdio.h>
int main() {
int x = 0x12345678;
char* p = (char*)&x;
if(p[0]==12){
printf("Big\n");
}
else{
printf("Little\n");
}
return 0;
}
方法二:利用联合体union的存放顺序是所有成员都从低地址开始存放这一特性进行检测
#include<stdio.h>
int main()
{
union uendian
{
int nNum;
char cLowAddressValue;
};
uendian u;
u.nNum = 0x12345678;
if ( u.cLowAddressValue == 0x12 ) {
printf("Big");
} else {
printf("Little");
}
return 0;
}
Linux 操作系统中相关的源代码:
static union { char c[4]; unsigned long mylong; } endian_test = {{ 'l', '?', '?', 'b' } };
#define ENDIANNESS ((char)endian_test.mylong)
分析:利用了联合体union 的特征,当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。
在C Programming Language 一书中对于联合体是这么描述的:
1)联合体是一个结构;
2)它的所有成员相对于基地址的偏移量都为0;
3)此结构空间要大到足够容纳最"宽"的成员;
4)其对齐方式要适合其中所有的成员;
所以,对 u.nNum 赋值后,可以从 u.cLowAddressValue 获取低地址的字节内容
六、Java设置大小端
public class HelloEndian {
public static void main(String[] args) {
ByteBuffer b = ByteBuffer.wrap(new byte[4]);
b.putInt(0x01020304);
System.out.println("Default-Endian: " + Arrays.toString(b.array()));
b = ByteBuffer.wrap(new byte[4]);
b.order(ByteOrder.BIG_ENDIAN);
b.putInt(0x01020304);
System.out.println("Big-Endian: " + Arrays.toString(b.array()));
b = ByteBuffer.wrap(new byte[4]);
b.order(ByteOrder.LITTLE_ENDIAN);
b.putInt(0x01020304);
System.out.println("Little-Endian: " + Arrays.toString(b.array()));
}
}
七、引用
https://segmentfault.com/a/1190000015566120
https://jocent.me/2017/07/25/big-little-endian.html
http://www.cnblogs.com/ziwuge/archive/2010/12/27/1917765.html