跟我学C++中级篇——字节序

一、什么是字节序

在谈字节序前讲个小故事,在小说《格列佛游记》中,有两派势力为了吃鸡蛋的时候儿到底是先打破大的一端还是打破小的一端展开了战争,而且这场战争持续了很久。后来,1980年,Danny Cohen在论文"On Holy Wars and a Plea for Peace"中引用了这个故事来处理一场关于数据传输时字节该以什么样的顺序进行传送的争论,所以在计算机的世界中也才有了字节序这个概念。
字节序,又称端序、尾序,英文单词为Endian,由于上面的鸡蛋的打破的大小端两种情况,所以这里也分成了little Endian(小头或小端模式),big Endian(大头或大端模式)。
这时候儿大家会不会突然明白,这不和汽车的左舵和右舵一样么?没有什么对错,只是设定的不同。
在开发者或者说常见的体系中一般是以小头字节序多,比如常见的x86体系的电脑基本都是小端模式。那么小端模式和大端模式是什么样子呢?大家都知道电脑的内存是从低地址向高地址增长的,对于存储一个数,如果在低地址存储低字节,高地址存储高字节那就是小端模式,反之为大端模式。举个例子就明白了:
一个十六进制的数:0x12345678(举十六进制是为了容易表述,和十进制一样),字节0x12是高字节,依次向低字节过渡,最低是0x78。如果内存是下面的情况:

 地址:0    1    2     3   (地址由低到高)
 数据:78   56   34    12
 则为小端模式,如果如下面的情况则为大端模式:
 地址:0    1    2     3   (地址由低到高)
 数据:12   34   56    78

大家有没有发现,大端模式符合人们的日常感官看法,因为人们一般书写数字都是从左到右从高位和低位的。如果大家想更直观的看,可以在VS或者其它的IDE中写个十六进制的变量,然后在内存观察器中查看,就可以看到上面的类似的场景了,非常直观。
常见的大小端的机器如下:
小端模式:x86,MOS Technology 6502,Z80,VAX,PDP-11等处理器
大端模式:Motorola 6800,Motorola 68000,PowerPC 970,System/370,SPARC(除V9外)等处理器
可配置模式:ARM, PowerPC (除PowerPC 970外), DEC Alpha, SPARC V9, MIPS, PA-RISC and IA64等处理器
说是常见,其实对大多数开发人员可能基本都不会遇到几个。

二、应用场景

这时如果大家跳脱出来,决定所有的数据都单纯以字节表示不就没这个大小端之争了么?确实,但计算机一开始的发展是没有标准的。就和全世界有米、尺、英尺等等一样。所以为了表示各种类型的数据,搞计算机的人怎么痛快怎么来嘛。你想看着舒服,我就想用着痛快;你想高位存储在高地址,低位存储在低字节,我就反着来。特别是需要用多个字节才能表示的整形(int,short,long等等)以及一些其它情况,大家肯定是各有所好,而且有的时候就是故意的对着干。
但对程序员来说,最常遇到的当然是小端,X86架构平台。即使开发移动程序,也是在PC上开发不是。
另外一个场景对开发者来说非常常见,网络编程,网络数据的传输是以大端模式进行的。所以大家还记不记得在传递端口号时要做一个转换,使用类似下面的函数:

//主机字节序到网络字节序
htons:unsigned short类型转换到网络序
htonl:unsigned long类型转换到网络序
//网络字节序到主机字节序
ntohs:unsigned short类型转换到主机序
ntohl:unsigned long类型转换主机序

其它的大小端模式,基本都是比较专业的开发才可能遇到了。

三、实际应用

在实际应用中,如果数据是以字节表示的,则不会有任何问题,即使在多个大小端的协议或者平台进行处理都不会有问题,比如图像的字节点阵。另外一个现象是,如果大小端传输的对称性,也没有问题。举个例子,当以小端字节传递数据,经过大端协议,但对方接收也是小端处理,正常情况下也不会有问题,这就是在进行了网络开发时,为什么只是把端口号啥的需要大端解析的数据转换一下,其它的传输的字节流数据(不是单纯的字节流,是小端表示数据转换过来的如int,long等)在接收后进行转换时不会出问题的原因。不过如果非对称的,则需要进行处理,比如发送是大端接收是小端,无论中间过多少手大小端,最终的部分都需要转换(中间部分只负责传输)。
为了保持安全,一般C/C++都有查看大小端的方法:
1、使用宏定义
BYTE_ORDER__宏,值为__ORDER_LITTLE_ENDIAN,即当前环境为小端模式,而值为__ORDER_BIG_ENDIAN__,则为大端模式
2、通过编程实现
可以使用联合体或位运算的方式来处理:

//联合体
union endian {
    int d;
    char order[4];
}Endian;
bool BigEndian() {
    Endian.d = 1;
    return (Endian.order[0] == 0);
}
//位
bool BigEndian() {
    int td = 1;
    return (*(char*)&td == 0);
}

3、C++中的库
使用std::endian,不过它在C++20才支持。使用的方法就不举例了,非常简单,大家可以查看一下帮助。

四、总结

其实字节序这种东西,如果不是经常和多个不同的架构平台打交道的话,其实意义真得不是多大。虽然经常应用的网络数据处理是大头的,但实际上网传输的基本都有库或者序列化,对开发者影响基本消除了。只有个别的如端口啥的可能需要自己处理,还都有专门的处理函数。所以对绝大多数开发者来说,都可以不需要专门注意这件事,当实际遇到相关的场景后再进行处理即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值