677-大端模式和小端模式详解

1、大端和小端核心是什么?

大端模式和小端是实际的字节顺序和存储的地址顺序对应关系的两种模式,总结如下:
大端模式:低地址对应高字节
小端模式:低地址对应低字节

不管是大端还是小端模式,我们在读取和存储数据的时候一定都是从内存的低地址依次向高地址读取或写入。

2、什么是存储顺序?

打个比方,我们定义一个数组,char array[5] = {0,1,2,3,4};
数组存储的内存分布如下:
在这里插入图片描述
不管是写还是读,我们都是只要根据这个首地址就能找到我们想要的元素内容,从这一点上讲,在存储的时候,我们依次去存储0、1、2、3、4。在读取的时候,我们也是从array[0]开始,依次读取0、1、2、3、4。

3、大端模式向小端模式发送数据

有了数据按照字节依次由低往高读取或存储这个前提,下面举例说明大端和小端的区别。
假设A要发送四字节的数据给B,
A的存储是按照大端模式,B的存储模式为小端模式。
A的存储1234内存分布如下图
在这里插入图片描述
因为A存储是按照大端模式,依据低地址对应高字节的规律,那么A要发送值int Value_A = 0x12345678。

Value_A将会由低地址依次发送给B,这里为什么是低地址,因为发送的开始,就是去读取数据,读取数据一定是从低地址开始读取得,那么B依次接收的值为0x12,0x34,0x560,x78。

B将接收到的值存储在如下图的内存中,存储的时候也是按照低地址往高地址开始依次存储
在这里插入图片描述
B接收完毕后,需要将内存中的值读取到Value_B中来,那么Value_B的值是多少呢?
B的存储是按照小端模式存储,低地址对应的低字节,那么int Value_B = 0x78563412
这里可能很多人会有疑问?不是读取是按照低地址从高地址依次读取的吗?为什么相同的存储,读出来的值却不一样了?

这里就是关键了。在读取0x12出来后,系统设定的模式是低地址对应低字节,我们的Value_B是int类型,是四个字节的数据,展示在我们面前的数据,一定是从左到右字节的顺序依次降低的,所以将0x12放在最低的字节,如下图。
在这里插入图片描述
即Value_B = 0x78563412;
Value_A 不等于Value_B,这就是为什么我们在网络通信的时候一定要进行字节序和确认了,必须保证A和B的字节序相同,如果不同,就需要使用字节序的转换函数。

4、字节序转换函数

1、htons 把unsigned short类型从主机序转换到网络序(host to network short)
2、htonl 把unsigned long类型从主机序转换到网络序(host to network long)
3、ntohs 把unsigned short类型从网络序转换到主机序(network to host short)
4、ntohl 把unsigned long类型从网络序转换到主机序(network to host long)

主机字节序一般都是小端(绝大多数,少部分也是大端存储的),网络字节序是大端存储的。

小端转大端方法1

UINT32  LE2BE(UINT8* dat, UINT8   len)
{
	UINT32  temp = 0, fact = 1;
	UINT8     i = 0;
	for (i = 0; i < len; i++)
	{
		temp += dat[i] * fact;
		fact *= 256;
	}
	return temp;
}

这段程序是什么意思呢?
我们还是举例说明。
在上面的案例中,如果B要给A发送int test_B = 0x12345678,B的存储就是小端模式,B要将0x12345678正确的发送给A,那么按照上面的发送和接收,A是不会接收到0x12345678的。
A会依次接收到0x78,0x56,0x34,0x12,不经过转换,低地址对应高字节,test_A = 0x78563412。
这里就需要按照上面进行转换。
上面的程序意图如下:

temp = 0x78*1 + 0x56*256 + 0x34 * 256*256 + 0x12*256*256*256
     = 120+22016+3407872+301989888
     = 305419896
     = 0x12345678

经过转换,A获取的值就是temp值,也就是B传送过来的值了。
看到这里,就算是把大端和小端的问题说明白了,实际上的问题核心就是一个是字节顺序,一个存储顺序。

小端转大端方法2

#include <stdio.h>

union endian//共用体(联合体)
{
	char a[4];
	int b;
}big;

//把int类型数据从小端序转成大端序
int big_endian(int n)
{
	char temp;
	big.b = n;
	//第1字节与第4字节交换
	temp = big.a[0];
	big.a[0] = big.a[3];
	big.a[3] = temp;

	//第2字节与第3字节交换
	temp = big.a[1];
	big.a[1] = big.a[2];
	big.a[2] = temp;
	return big.b;
}

void main()
{
	int n = 0x12345678;
	printf("小端序:%#x\n", n);
	n = big_endian(n);
	printf("大端序:%#x\n", n);
}

小端转大端方法3

#include <stdio.h>
 
//把int类型数据从小端序转成大端序
void main()
{
	int n=0x12345678;
	char *p=(char *)&n;
	printf("小端序:%#x\n",n);
	n=(*p)<<24|(*(p+1))<<16|(*(p+2))<<8|(*(p+3));
	printf("大端序:%#x\n",n);
}

如何判断机器的字节序1

#include <stdio.h>
int main (void)
{
	union
	{
		short i;
		char a[2];
	}u;
	u.a[0] = 0x11;
	u.a[1] = 0x22;
	printf ("0x%x\n", u.i);  //0x2211 为小端  0x1122 为大端
	return 0;
}
输出结果:
0x2211

union 型数据所占的空间等于其最大的成员所占的空间。对 union 型的成员的存取都是相对于该联合体基地址的偏移量为 0 处开始,也就是联合体的访问不论对哪个变量的存取都是从 union 的首地址位置开始。
联合是一个在同一个存储空间里存储不同类型数据的数据类型。这些存储区的地址都是一样的,联合里不同存储区的内存是重叠的,修改了任何一个其他的会受影响。

如何判断机器的字节序2

在这里插入图片描述

如何判断机器的字节序3

在这里插入图片描述

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值