说明
- 大小端问题由来:《格列佛游记》小说中,小人国为水煮蛋该从大的一端(Big-End)剥开还是小的一端(Little-End)剥开而争执,争执的双方分别被称为“大端派”和“小端派”。
计算机领域
- 大小端问题引申到计算机领域:将数据存入大于一个字节大小的内存中,例如:0x1存入4字节大小内存,数据是从内存高地址开始存储还是从内存低地址开始存储?一个字节的内存就不存在这个问题。
- 正因为有以上争执,CPU既有采用大端模式的也有采用小端模式的,例如:arm,power,mips早期都坚持大端,但是也许是为了怕作死,也做了小端,不过由于X86是小端,大家上学时候学计算机原里大多也是学的小端,当前常用的大部分是小端模式;但是由于既有大端CPU也有小端CPU,软件需要兼容处理。
疑惑点
- 数据以及内存的低位高位是以bit为单位还是以byte为单位?
0x12345678
内存地址 高 ~ 低
大端:0x78563412
小端:0x12345678
- 如果以bit为单位,即使是单字节也会受大小端影响,如下:
0x12
内存地址 高 ~ 低
小端:0001 0010
大端:0100 1000 // 0x48
- 由于当前手上没有大端设备,但是网络字节序是采用的大端,通过网络字节序验证如下:
int a = 0x12345678;
int b = htonl(a);
printf("%08x", b);
* 在小端设备上运行结果是:0x78563412
- 由测试可知:数据以及内存的高位和低位是以Byte字节为单位。
特殊情况
- 虽然CPU既有大端也有小端,但是网络传输为了兼容,字节序采用大端模式。
理解
什么是大小端模式
- 内存存储数据时,数据的高低位和内存地址的高低位对应关系;直白点说:内存存储数据是从低地址开始存储,还是从高地址开始存储。
区别
- 大端模式:数据从高地址开始存储,高位数据存在于内存低位地址,低位数据存在于内存高位地址。
- 小端模式:数据从低地址开始存储,高位数据存在于内存高位地址,低位数据存在于内存低位地址。
- 例如:0x1存储在4Bytes内存中
内存地址: 0x03 0x02 0x01 0x00
小端: 0x00 0x00 0x00 0x01
大端: 0x01 0x00 0x00 0x00
注意点
- 大小端是CPU控制内存的存储方式的不同,和内存区域无关
- 大小端是CPU控制内存的存储方式的不同,并不受数据类型影响
- 大小端是将数据存储域多个字节内存空间的存储方式,单个字节内存空间没该问题; 容易被误解为单字节类型数据不受大小端影响,只有多字节类型数据受影响。
- 例子
char *c = "hello world";
* 如果是小端CPU,不仅仅c指向的内存中存储的地址是按小端存储的,并且“hello world”也是按小端存储的,'h'在低地址,'d'在高地址,大端相反。
char a[10];
* a是字节数组,如果是小端CPU,a[0]在低地址,a[9]在高地址,大端相反。
char *c = "hello world";
char *p = c;
for (int i = 0; i < strlen(c); i++){
printf("%c", *p);
p++;
}
* 为什么可以通过p++遍历该字符串?其实这是小端CPU上的做法,大端应该改成p--;原因:p++只是内存地址的增加,刚好匹配小端的存储方式,和大端的存储方式相反。
判断大小端
- 位移法
short a = 1;
(a >> 8)?printf("Big"):printf("Little");
- 地址: 高地址 <<====== 低地址
- 小端模式存放:0000 0001 >> 8 == 0000 0000
- 大端模式存放:0001 0000 >> 8 == 0000 0001
个人总结
- 网上有很多博客有描述判断大小端的方法,例如:位移法,强制转换法,共用体法,但是我对其中的几种方法存在怀疑,原因是:
- 博客中只看到程序在小端设备设备上的运行结果,却从来没有人使用大端设备来验证程序是否能判断正确。
- 感觉都是站在小端的角度思考大端。
* 强制转换法
short a = 1;
char b = a;
(b)?printf("Little"):printf("Big");
- 博客解释如下:数据类型强制转换,本质还是取低8位数;a = 1 ,变成二进制为:0000 0001,强转为char时,保留unsigned short a变量的低8位。所以这样就更加明了了。因为1低位数据,如果char c不为0,表明1存放在低位,即是小端模式;如果char c变量为0,表明1存放在高位,即是大端模式。
- 个人理解:这里short类型转换为char类型,数据类型强制转换,本质还是取低8位数,我理解的低8位,应该是数据的低8位,而不是上面解释的内存低8位,小端数据的低8位刚好是内存的低8位,但是大端却相反,不能因为小端是内存低8位,大端就也是内存低8位;我也不认为,CPU会出现基础问题:值超出范围的数据类型强制转换,值丢失可以理解,但是数据1即在char范围内也在short范围内,强制类型转换后,值会丢失!
- 文章的目的并不是为了学习如何判断大小端(当前常用的CPU都是小端,没有兼容的需求,判断也没意义),而是为了理解不同模式下,内存数据的存储方式。