大小端字节序与序列化

大端字节序

这是一种更适合人类读取数据的方式
举个例子,有如下数据:
0x12345678
高—————>低 位
按8位为一个字节逻辑(byte进行网络传输时不需要进行网络字节序转换)把上面数据拆分成4部分:
0x12   0x34   0x56   0x78

地址: 0x100 0x101 0x102 0x103
|———|—12—|—34—|—56—|—78—|——|
从左到右:低地址到高地址
从左到右:高位到低位

记忆方法:低高高低,这是一种便于人类读取的方式。像120,百位十位个位,也是从高位到低位进行读的:一百二十


小端字节序

这是一种更适合机器读取的方式,反人类的方式。
还是如上的数据再这边的地址数据显示是这样的:
地址: 0x100 0x101 0x102 0x103
|———|—78—|—56—|—34—|—12—|——|
从左到右:低地址到高地址
从左到右:低位到高位

记忆方法:低低高高(对于大小端的记忆其实理解最为重要,理解后就可以很深刻的记住了)
计算机都是从低位开始计算的,所以计算机电路是先处理低位字节的,效率也会高些。
计算机字节序,通常都会使用小端字节序,当然有些系统例外。

网络字节序的转换

通常网络传输惯于使用大端字节序,也就是人类习惯的一种字节序。当然进行网络字节序的转换还有个目的是为了保证传输格式的统一。
因为虽然主机字节序大多数使用小端字节序,但是还是有一小部分的使用大端字节序的,为了保证不同字节序的两台机子能够进行通信,则可以都先转换为统一的网络字节序进行数据的接收和传输。
如C++中的: htonl、ntohl等,都是进行字节序的转换。
前者主机字节序到网络字节序,后者网络字节序到主机字节序

如何用代码判断大小端

#include <iostream>

// 判断大端字节序 方法一
bool IsBigEndian()
{
    int data = 0x12345678;
    char *tar = (char *)&data;
    return (tar[0] == 0x12);
}

// 判断小端字节序 方法二:利用联合体
union UEndian
{
    char a[4];
    int val;
};

bool IsLittleEndianByUnion()
{
    UEndian data;
    data.val = 1;
    return (data.a[0] == data.val);
}

int main(void)
{

    std::cout << "fun1 is big endian =======" << (int)IsBigEndian() << std::endl;
    std::cout << "fun2 is little endian =======" << (int)IsLittleEndianByUnion() << std::endl;

    return 0;
}


输出结果如下:

fun1 is big endian =======0
fun2 is little endian =======1

从如上的输出可以看出判断大端字节序返回的是false,而判断小端字节序返回的是true,由此可以证明我本地(macOS)的字节序为小端字节序。

那么字节序转换跟序列化有什么关系呢

对于这两者的概念,其实他们俩没有直接的关系,挺多人可能会搞混,先搞清楚什么是序列化?

序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程

从我个人理解来看
如果学过计算机的童鞋都知道,其实计算机只能识别0跟1(一个字节就是八个0或1,对于C而言一个字节=一个字符,多个字符就形成了字符串;相比起数值啥的,他们只是多了个规定多少个字节为一个数值语言层面上的东西,对于计算机来说它是不懂的,但他们本质是一样的,只需要把数值转换成多个字节那么计算机就可以识别了。)
目前的序列化协议其实挺多的,二进制流、protobuf、xml、json等等,这些传输方式存在是为了让变量啥的能够通过某种逻辑转换能传输到其他终端,再通过相反的逻辑进行解析,并在其他端可以使用。
总结:我们看到的形形色色的变量呀对象啥的,其实计算机是不认识的,所以这中间需要翻译,那么翻译其实就是序列化/反序列化。

还不明白也不要紧,以下是使用二进制的形式进行序列化,先来看看下面一段代码就明白了。


void Push(uint32 val)
{
    append<uint32>(htonl(val));
}

// 这里省略了一些调用
void append(const uint8 *src, size_t cnt)
{
	if (!cnt) return;
	
	if(_storage.size() < _wpos + cnt)
		_storage.resize(_wpos + cnt);
	if (_storage.size() > _wpos)
	{
		memcpy(&_storage[_wpos], src, cnt);
		_wpos += cnt;
	}
}

从上面的代码中我们可以看出,对于Push方法,我们在插入 val 的时候,其实是先将 val 通过htonl进行了本地字节序到网络字节序的转换这个过程主要是防止不同端因为本地字节序的不同导致接收后数据错误),后通过append方法,将字节序转换后的值插入到storage这个数组里面(当然我这里用的std::vector),其实这个插入过程就是一个序列化的过程。

如有说的不对的地方,欢迎批评指错~

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 大端(Big-Endian)模式:数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。小端(Little-Endian)模式:数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。 ### 回答2: 串口通信的大小端字节序取决于传输的数据类型。在串口通信中,数据通常以字节的形式进行传输。对于字节的存储方式,有两种常见的字节序:大端字节序(Big-Endian)和小端字节序(Little-Endian)。 大端字节序是指将数据的高位字节存储在内存的低地址中,而将数据的低位字节存储在内存的高地址中。这种存储方式常用于网络传输和大部分的大型计算机中。 小端字节序则是将数据的低位字节存储在内存的低地址中,而将数据的高位字节存储在内存的高地址中。这种存储方式常用于一些小型计算机和个人电脑中。 在串口通信中,字节的传输是按照其在内存中的存储顺序进行的。如果通信的两端采用相同的字节序,则数据能够正确地传输。然而,如果通信的两端采用不同的字节序,则数据在接收端可能会发生错误的解析。 为了确保正确的数据传输,通常需要在通信的两端进行字节序的转换。这可以通过将字节按照反转的方式进行重新排列来实现,从而使得传输的字节顺序与接收端相符。 总之,串口通信的大小端字节序是通过对字节的存储方式进行约定来确定的。如果通信双方采用相同的字节序,数据能够正确地传输。否则,需要进行字节序的转换以确保数据的正确解析。 ### 回答3: 串口通信的大小端字节序是指数据在传输过程中的存储顺序。在串口通信中,一个字节通常由8位二进制数据组成,而字节序指的是这8位二进制数据的存储顺序。 小端字节序(Little-endian)是指低位字节存储在内存的低地址处,而高位字节存储在内存的高地址处。在小端字节序中,数据的字节排列顺序是从低位到高位的顺序。例如,十六进制数0x12345678的存储顺序为0x78 0x56 0x34 0x12。 大端字节序(Big-endian)则是指高位字节存储在内存的低地址处,而低位字节存储在内存的高地址处。在大端字节序中,数据的字节排列顺序是从高位到低位的顺序。例如,十六进制数0x12345678的存储顺序为0x12 0x34 0x56 0x78。 在串口通信中,数据的传输是以字节为单位的。当数据从发送端传输到接收端时,发送端和接收端需要使用相同的大小端字节序,以保证数据的正确传输和解析。 需要注意的是,串口通信中的大小端字节序是由通信设备或协议决定的,而不是由计算机的架构决定的。所以,在进行串口通信时,需要了解所使用的通信设备或协议的字节序要求,并进行相应的处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值