目录
什么是大小端
-
首先明白字节序,字节序指占内存超过1个字节类型的数据在内存中存放顺序,通常有大端和小端两种顺序:
-
大小端是内存存储字节的两种方式,一个是大端存储,一个是小端存储。
-
网络字节序使用的是大端方式,大部分计算机使用的是小端模式。
-
采用大小模式对数据端进行存放的主要区别在于存放的字节顺序,大端方式将高位存放在低地址,小端方式将高位存放在高地址。
-
采用大端方式进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。
- 大端存储:指低字节数据存放在内存地址,高字节数据存放在高地址处
- 小端存储:指低字节数据存放在高地址处,高字节数据存放在内存地址处。
大小端的意义
主机序、网络序
1、主机序(大部分计算机采用 小端模式)
不同的CPU处理数据有不同的字节序类型,将一台设备的字节序类型称作为主机序
2、网络序(大端模式)
网络上以字节流的方式传输数据,在传输过程中的字节序类型称为网络序。与其他具体设备的CPU/OS类型无关,保证了在不同设备间传输时能被正确解析。
网络序固定采用大端模式,所以在大端设备中主机序和网络序是相同不需要转换
如何确定大小端
使用联合体(共同体)来检查
联合体
在C语言中,联合体也被称为共用体,它的关键字是union。
定义一个联合类型的一般形式为:
union 联合名
{
成员表
};
联合体占用的字节数与其成员中最大数据类型占用的字节数相等,例如:
union Un
{
int a;
char b;
}un;
该联合体中成员a是int类型占四个字节,成员b是char类型占一个字节,所以该联合体Un在内存中占四个字节,并且联合体中的成员能够公用一个内存空间,内存可以这样表示:
由于联合体的特性,通过截取b,即可知道字节序的存放方式,即可知道是大端机还是小端机。
代码检测
#include <stdio.h>
int Check_sys()
{
union Un
{
char c;
int i;
}un;
un.i = 1;
//如果是大端机则是0x1000 0000 0000 0000
//如果是小端机则是0x0000 0000 0000 0001
return un.c;
}
int main()
{
int ret=Check_sys();//由于联合体的特性,大端机ret=0(0000),小端机ret=1(0001)
if (1 == ret)
{
printf("当前模式为小端存储\n");
}
else
{
printf("当前模式为大端存储\n");
}
return 0;
}
输出结果:
使用指针检测
一个 int 的指针每次偏移的是4个字节,而一个 char 类型的指针每次偏移的是一个字节,那么就可以类似于上面联合体的方式截取内存中的字节来进行大小端的判断
int Check_sys()
{
int a = 1;
char* p = (char*)&a;
return *p; //大端返回0,小端返回1
}
int main()
{
int ret = Check_sys();
if (1 == ret)
{
printf("当前模式为小端存储\n");
}
else
{
printf("当前模式为大端存储\n");
}
return 0;
}
检测结果
![image](/i/l/?n=23&i=blog/3033909/202308/3033909-20230814002037259-546590832.png)
什么时候需要转换大端与小端
-
内存存储以Byte为单位,当涉及一个Byte的变量存储时是没有区分的
-
c语言中内置的数据类型有int、double、short等多字节类型,存储该类型时将有两种方式,即上述的大端(低地址高位)与小端(低地址低位),这类的变量在由host1发送至host2时,需要进行大小端转换,即host1发往网络进行hton,host2接收时进行网络序转主机序。
1、两者转换
为实现不同设备间的通讯,数据能够被正确的解析,需要进行主机序与网络序之间的转换。
相同字节序的设备进行网络通信时无需字节转换,反正必须进行字节序转换
2、转换函数:
字节序转换函数会根据当前平台存储模式做响应正确的转换,如果当前平台是大端,则直接返回不进行转换,如果当前平台是小端,会将接收得到的网路字节序进行转换
这要求硬件工程师在硬件设计中注意端模式的转换。
3、如果场景是host1发送tcp报文报文至host2,但是host2接收时并未进行网络序转主机序,那么将会出现什么情况?
报文字节流是网络字节序,host2接收后并未进行主机序转换(仍以大端存储),此时如果进行报文解析(以小端方式解析),解析出来的报文将会出错,以以太帧类型为例
uint8 *raw_buf = m_buf; //指向报文字节流,未进行大小端转换
uint16 eth_type = (uint16 *)(raw_buf + 12); //eth_type存储报文类型,未进行大小端转换
以太帧类型网络字节序为大端0x0800
当原封不动到达主机端,eth_type由强转获取,即内存存储的仍为0x08 0x00
(gdb) x/2xb ð_type
0x7fffffffe50e: 0x08 0x00
但是,需要注意的是此时host2将以小端方式解析该变量,即内存0x7fffffffe50e: 0x08 0x00将解析为0x0008
(gdb) p/x eth_type
$3 = 0x8
总结
-
大端和小端只是两种存储方式,但是无论如何存储,只要按照各自的解析方式,解析出来的含义是一致的就OK。例如host1为小端存储,host2为大端存储,但是两个计算机中运行的c程序中变量eth_type永远都是0x0800这种我们熟知含义的协议号。所以程序员不用过分关注大小端。
-
不进行大小端转换,将会导致原来大端模式存储的数据,却以小端存储方式来解读,必然会导致错误。