引言:什么是大小端模式
在计算机通信过程中,经常会遇到一个问题,是先发送数据的高字节还是先发送低字节呢?比如我们经常可以在网络通讯协议中,都能听到”大端(big endian)模式”这一词,与之对应的是"小端模式(little endian)",发送方和接收方必须按照相同的字节顺序来处理数据,否则就会出现错误。
而今天我们主要讲的是计算机存储系统的大小端,在计算机内存/硬盘中,数据是以字节为单位存储的,于是乎有一个32位的二进制在内存中存储时就有2种方式分布:1.高字节对应高地址(大端模式) 2.高字节对应低地址(小端模式), 以下示意图,感受下区别:
经典笔试题:测试当前机器的大小端模式
上一篇我们详细讲解了共用体union的一些概念,如定义,使用,内存分布等等知识点,今天我们就趁热打铁,使用共用体union来测试我们机器的大小端模式,先贴代码:
#include <stdio.h>
union test
{
int a;
char b;
};
//返回值:
// 1: 小端模式
// 0: 大端模式
int Is_Little_Endian(void)
{
union test t1;
t1.a = 1;
return t1.b;
}
int main(void)
{
int ret = Is_Little_Endian();
if(ret == 1)
printf("我的机器是小端模式!\n");
else
printf("我的机器是大端模式!\n");
return 0;
}
代码是不是很简单呢~, 我们来简单分析下:
1. 首先我们定义函数int Is_Little_Endian(void),其作用就是来测试当前机器的大小端的
- 我们在函数体中定义了共用体t1, 并初始化了t1.a=1; 并直接返回了t1.b
- 我们在主函数main中定义ret变量来接收上面函数返回的t1.b的值
- 根据ret的值,我们判断机器的大小端,并打印相关信息
相信这里大家有个疑惑,凭啥就能根据t1.b的值就能判断机器的大小端勒? 这里有一个很重要的概念,即共用体的本质:共用体内元素不独立,元素之间共享同一块内存空间,只是解析不同!!!,我们通过下图来感受下其道理:
注意共用体t1中元素b是char类型,只占用第一个字节(内存地址为0x8的一个格子),因此我们可以根据t1.b的值来判断机器的大小端。
编译运行:
我的测试机器就是英特尔的x86_64,就是小端模式,与运行结果一致。
到这里如果大家还是怀疑上面代码的测试的可靠性,我特意下载了keil c51来验证下,同样我们将以上的代码原封不动贴到Keil4 C51(机器设置为AT89C51RC,相信大家对这款单片机型号不陌生吧, 满满的回忆~~):
OK,我们软件仿真下:
简单分析下:
1. 分别打了2个断点(上图红色标记处),然后单步运行
- 可以看到单步到if判断语句时,跳转到else之后的代码,打印了printf("我的机器是大端模式!\n");
- 即说明AT89C51RC是大端模式
用指针方式测试大小端
#include <stdio.h>
union test
{
int a;
char b;
};
//返回值:
// 1: 小端模式
// 其他: 大端模式
int Is_Little_Endian2(void)
{
int a = 1;
char b = *((char *)(&a));
return b;
}
int main(void)
{
int ret = Is_Little_Endian2();
if(ret == 1)
printf("我的机器是小端模式!\n");
else
printf("我的机器是大端模式!\n");
return 0;
}
简单分析:
1. 在Is_Little_Endian2函数体中, 我们定义int a,并赋值为1
- 我们将a取地址后,强制转化成char *类型,即由原来执行一个int类型(4个格子)变为指向一次char类型(1个格子)的内存空间。然后解引用后赋值给b
- 函数返回b的值,并根据b的值判断机器的大小端
编译运行:
结果还是一样的,同样用内存示意图加深理解:
小结
大端模式和小端模式本身没有对错,没有优劣,理论上按照大端或小端都可以,但是要求必须存储时和读取时按照同样的大小端模式来进行,否则会出错。
现实的情况就是:有些CPU公司用大端(譬如C51单片机;有些CPU用小端(譬如ARM)(起始大部分是用小端模式),当不知道当前环境是用大端模式还是小端模式时就需要用代码来检测当前系统的大小端。