字节序、大端序与小端序及其相关转换

2024-03-01 21:48

本文主要是介绍字节序、大端序与小端序及其相关转换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大端序与小端序及其相关转换

 

一、字节序定义

字节序,为字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据当然就没有顺序可言了,其实大部分人在实际的开发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。

二、大端序与小端序

字节序分为两类:Big-Endian和Little-Endian。

1. Little-Endian(小端序)就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
2. Big-Endian(大端序)就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

unsigned int整型数据0x12345678为例,其大端序、小端序的存储内容如图所示

如0x01000000就为内存的低地址端,0x01000003就为内存的高地址端

而数据0x12345678中靠近左边的为高位字节,靠近右边的为低位字节,也就是说0x12为高位字节,0x78为低位字节

	unsigned int i= 0x12345678;unsigned char* p = (unsigned char*)&i;printf("%x\n", p[0]); //打印出16进制的78printf("%x\n", p[3]); //打印出16进制的12

如代码所示,对 i 的地址进行类型转换,指针p指向的位置是 i 在内存中存储空间的低地址端的位置,又因为个人电脑内存中存储数据是以小端序来存储的,所以内存低地址端存的是低位字节,所以p[0]以16进制来打印,打印出78,p[3]打印出12

三、网络字节序

 网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于 TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。比如,以太网头部中2字节的“以太网帧类型”,表示后面数据的类型。UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节(即:高位字节存放在低地址处);由此可见,多字节数值在发送之前,在内存中因该是以大端法存放的; 所以说,网络字节序是大端字节序; 比如,我们经过网络发送整型数值0x12345678时,在80X86平台中,它是以小端发存放的,在发送之前需要使用系统提供的字节序转换函数htonl()将其转换成大端法存放的数值。对于ARP请求或应答的以太网帧类型来说,在网络传输时,发送的顺序是0x08,0x06。在内存中的映象如下图所示:

栈底 (高地址)
---------------
0x06 -- 低位字节 
0x08 -- 高位字节
---------------
栈顶 (低地址)
该字段的值为0x0806。按照大端方式存放在内存中。

四、内存空间中的相关布局

关于内存空间布局情况的说明,大致如下图:

----------------------- 最高内存地址 0xffffffff
 | 栈底
 .
 .              栈
 .
  栈顶
-----------------------
 |
 |
\|/

NULL (空洞)

/|\
 |
 |
-----------------------
                堆
-----------------------
未初始化的数据
----------------(统称数据段)
初始化的数据
-----------------------
正文段(代码段)
----------------------- 最低内存地址 0x00000000

以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢?看下图:
栈底 (高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
栈顶 (低地址)

现在我们弄清了高低地址,接着来弄清高/低字节,如果我们有一个32位无符号整型0x12345678,那么高位是什么,低位又是什么呢?其实很简单。在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。就拿 0x12345678来说,从高位到低位的字节依次是0x12、0x34、0x56和0x78。

高低地址和高低字节都弄清了。我们再来回顾一下Big-Endian和Little-Endian的定义,并用图示说明两种字节序:
以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:

Big-Endian: 低地址存放高位,如下图:
栈底 (高地址)
---------------
buf[3] (0x78) -- 低位字节
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) -- 高位字节
---------------
栈顶 (低地址)

Little-Endian: 低地址存放低位,如下图:
栈底 (高地址)
---------------
buf[3] (0x12) -- 高位字节
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) -- 低位字节
---------------
栈顶 (低地址)

在现有的平台上Intel的X86采用的是Little-Endian,而像Sun的SPARC采用的就是Big-Endian。

五、网络通讯字节的转换

 相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。

 原因如下:网络协议规定接收到得第一个字节是高字节,存放到低地址所以发送时会首先去低地址取数据的高字节。小端模式的多字节数据在存放时,低地址存放的是低字节,而被发送方网络协议函数发送时会首先去低地址取数据(想要取高字节,真正取得是低字节),接收方网络协议函数接收时会将接收到的第一个字节存放到低地址(想要接收高字节,真正接收的是低字节),所以最后双方都正确的收发了数据。而相同平台进行通信时,如果双方都进行转换最后虽然能够正确收发数据,但是所做的转换是没有意义的,造成资源的浪费。而不同平台进行通信时必须进行转换,不转换会造成错误的收发数据,字节序转换函数会根据当前平台的存储模式做出相应正确的转换。当前平台为大端

六、大小端序的判断

(1)通过指针判断(返回真则为小端序,返回假则为大端序)

bool isLittleEndian()
{unsigned int i = 0x12345678;unsigned char* c = (unsigned char*)&i;return(*c == 0x78); //判断是否是低位字节存内存低地址  *c取得就是内存中存放i的低地址
}

(2)通过联合体判断(返回真则为小端序,返回假则为大端序)

bool isLittleEndian()
{union{int i;char c;}udata;udata.i = 1;return(udata.c == 1);
}

(3)linux环境下,通过htonl等函数直接判断

#include <arpa/inet.h>
bool isLittleEndian()
{return (1 != htonl(1));
}

七、大小端的转换

(1)自定义函数

//短整型大小端互换
#define BigLittleSwap16(A) ((((uint16_t)(A) & 0xff00) >> 8 ) | \\(((uint16_t)(A) & 0x00ff) << 8 ))//长整型大小端互换
#define BigLittleSwap32(A) ((((uint32_t)(A) & 0xff000000) >> 24) | \\(((uint32_t)(A) & 0x00ff0000) >> 8 ) | \\(((uint32_t)(A) & 0x0000ff00) << 8 ) | \\(((uint32_t)(A) & 0x000000ff) << 24))

结合判断大小端的函数,如果本机是大端,则可以直接返回,如果本机是小端,则需要进行字节序的转换,或者进行网络数据的转换,再返回

(2)winsock.h头文件中的函数

uint32_t htonl(uint32_t hostlong);//32位的主机字节序转换到网络字节序
uint16_t htons(uint16_t hostshort);//16位的主机字节序转换到网络字节序
uint32_t ntohl(uint32_t netlong);//32位的网络字节序转换到主机字节序
uint16_t ntohs(uint16_t netshort);//16位的网络字节序转换到主机字节序

拿htonl和ntohl来分析,htonl函数的内部实现原理是这样,先判断主机是什么模式存储,如果是大端模式,就跟网络字节序一致,直接返回参数即可,如果是小端模式,则把形参转换成大端模式存储在一个临时参数内,再把临时参数返回;而ntohl函数的实现原理也是一样的过程,但是要注意它的参数,参数是网络字节序,就是大端模式存储,因此当判断主机是大端模式的时候,会直接返回,因为该函数默认会认为形参是网络字节序,把它当大端模式来看,如果判断主机是小端模式,就会将实参做转换,转换的过程并不复杂,就是逆序存储各个字节的数据,所以结果就被转换。

说到这里,可以看出一个规律来,就是如果主机与网络字节序不一致(也就是小端模式),这四个函数的返回值与传递进去的实参值的字节排序肯定是逆序的,所以返回值绝对不等于实参值,例如htonl(1)的结果肯定不是1,而如果主机与网络字节序一致(也就是大端模式),则这四个函数根本就没有做转换操作,而是直接返回实参值,这样他们的返回结果就肯定与实参值相同,即htonl(1)的结果是1。

这样,我们就得到了一个非常简便的判断系统是什么模式的方法,就是直接利用这四个函数来判断,如:

if(1 != htonl(1)) 
{//小端模式,作相应处理
}
else 
{//大端模式,作相应处理
}

或者直接用一个判断if(1 != htonl(1)),只有主机字节序与网络字节序不一致时,才调用那些函数去转换,否则不需要处理,这样可以减少多余的函数调用。

八、其他相关转化

(1)将整数按照“大端序”格式存储在数组中

/*
*    Function:        ConverseUItoBeArray
*    Description:    将无符号整数转换成“大端序”存储的无符号字符数组
*    Parameter:        srcData        --[in]    源整数
*                    desBeArray       --[out]    目标“大端序”存储的数组数据
*    Return            0    成功
*                    非0    失败
*    Note:            
*    Other:
*/
int MULCONVERSE_CALL ConverseUItoBeArray(unsigned int srcData,unsigned char *desBeArray)
{if (desBeArray == NULL_POINT){return ERR_NULL_POINT;}desBeArray[0] = (unsigned char)(srcData>>24);desBeArray[1] = (unsigned char)(srcData>>16);desBeArray[2] = (unsigned char)(srcData>>8);desBeArray[3] = (unsigned char)srcData;return _SUCCESS;
}

(2)将整数按照“小端序”格式存储在数组中

/*
*    Function:        ConverseUItoLeArray
*    Description:    将无符号整数转换成“小端序”存储的无符号字符数组
*    Parameter:        srcData        --[in]    源整数
*                    desLeArray       --[out]   目标“小端序”存储的数组数据
*    Return            0    成功
*                    非0    失败
*    Note:            
*    Other:
*/
int MULCONVERSE_CALL ConverseUItoLeArray(unsigned int srcData,unsigned char *desLeArray)
{if (desLeArray == NULL_POINT){return ERR_NULL_POINT;}desLeArray[3] = (unsigned char)(srcData>>24);desLeArray[2] = (unsigned char)(srcData>>16);desLeArray[1] = (unsigned char)(srcData>>8);desLeArray[0] = (unsigned char)srcData;return _SUCCESS;
}

 

这篇关于字节序、大端序与小端序及其相关转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/763935

相关文章

.NET利用C#字节流动态操作Excel文件

《.NET利用C#字节流动态操作Excel文件》在.NET开发中,通过字节流动态操作Excel文件提供了一种高效且灵活的方式处理数据,本文将演示如何在.NET平台使用C#通过字节流创建,读取,编辑及保... 目录用C#创建并保存Excel工作簿为字节流用C#通过字节流直接读取Excel文件数据用C#通过字节

基于C#实现将图片转换为PDF文档

《基于C#实现将图片转换为PDF文档》将图片(JPG、PNG)转换为PDF文件可以帮助我们更好地保存和分享图片,所以本文将介绍如何使用C#将JPG/PNG图片转换为PDF文档,需要的可以参考下... 目录介绍C# 将单张图片转换为PDF文档C# 将多张图片转换到一个PDF文档介绍将图片(JPG、PNG)转

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

log4j2相关配置说明以及${sys:catalina.home}应用

${sys:catalina.home} 等价于 System.getProperty("catalina.home") 就是Tomcat的根目录:  C:\apache-tomcat-7.0.77 <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" /> 2017-08-10

Node Linux相关安装

下载经编译好的文件cd /optwget https://nodejs.org/dist/v10.15.3/node-v10.15.3-linux-x64.tar.gztar -xvf node-v10.15.3-linux-x64.tar.gzln -s /opt/node-v10.15.3-linux-x64/bin/npm /usr/local/bin/ln -s /opt/nod

git ssh key相关

step1、进入.ssh文件夹   (windows下 下载git客户端)   cd ~/.ssh(windows mkdir ~/.ssh) step2、配置name和email git config --global user.name "你的名称"git config --global user.email "你的邮箱" step3、生成key ssh-keygen

zookeeper相关面试题

zk的数据同步原理?zk的集群会出现脑裂的问题吗?zk的watch机制实现原理?zk是如何保证一致性的?zk的快速选举leader原理?zk的典型应用场景zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?zk对事物的支持? 1. zk的数据同步原理? zk的数据同步过程中,通过以下三个参数来选择对应的数据同步方式 peerLastZxid:Learner服务器(Follo

PDF 软件如何帮助您编辑、转换和保护文件。

如何找到最好的 PDF 编辑器。 无论您是在为您的企业寻找更高效的 PDF 解决方案,还是尝试组织和编辑主文档,PDF 编辑器都可以在一个地方提供您需要的所有工具。市面上有很多 PDF 编辑器 — 在决定哪个最适合您时,请考虑这些因素。 1. 确定您的 PDF 文档软件需求。 不同的 PDF 文档软件程序可以具有不同的功能,因此在决定哪个是最适合您的 PDF 软件之前,请花点时间评估您的