添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

一、字节序定义

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

二、大端序与小端序

字节序分为两类: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进制的78 printf("%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)) //小端模式,作相应处理 //大端模式,作相应处理

或者直接用一个判断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; 计算机硬件有两种储存数据的方式:大端 字节序 (big endian)和小端 字节序 (little endian)。 举例来说,数值0x2211使用两个字节储存:高位字节是0x22,低位字节是0x11。 大端 字节序 :高位字节在前,低位字节在后 1.Big-Endian(大端):高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 2.Little-Endian(小端):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 三、高低地址与高低字节 高低地址: C程... x86/x64架构(Intel/AMD处理器)、ARM处理器(可配置)。网络传输(网络 字节序 )、Java虚拟机、PowerPC处理器。“大端高位先,小端低位前;网络用大端,Intel是小端。类似书写顺序:从左到右,高位在前(如数字。类似反序书写:从右到左,低位在前(如数字。(从左到右为低地址到高地址)。:高位在前,适合人类阅读习惯。“大”对应“高位优先”,即。“小”对应“低位优先”,即。:低位在前,硬件处理更高效。放在内存起始(低地址)。放在内存起始(低地址)。,低位字节存于高地址。,高位字节存于高地址。 不同机器内部对变量的字节存储顺序不同,有的采用大端模式(big-endian),有的采用小端模式(little-endian)。 大端模式是指高字节数据存放在低地址处,低字节数据放在高地址处。 小端模式是指低字节数据存放在低地址处,高字节数据放在高地址处。 举例来说,数值0x2211使用两个字节储存:高位字节是0x22,低位字节是0x11。 大端 字节序 :高位字节在前,低位字节在后,这是人类读写数值的方法。 小端 字节序 :低位字节在前,高位字节在后,即以0x1122形式储存。 0x1234567的大 转载请标明出处:https://blog.csdn.net/qq_29621351/article/details/81238589 字节序 ,顾名思义就是计算机存储基本数据类型时 字节的排列顺序,计算机存储字节的顺序分为两种:大端 字节序 和小端 字节序 ,存储方式为 大端 字节序 的0x11在硬件地址的高位,而小端 字节序 的0x11在地址的低位。如图 数值:0x11223344 大端:0x1... 最近在做客户项目的时候,遇到了不同厂家的安防相机输出的数据大 小端序 不一致的情况,导致在使用同一种方式处理时,出现了错误。虽然问题已经解决,还是顺手把大 小端序 的知识简单梳理一下。 1. 大端序 小端序 的概念 我们知道,计算机在存储数据时,是以字节为单位的,每个地址对应一个字节。但在现代编程语言中,使用的数据类型往往需要1、2、4、8个字节,那么就涉及到这些数据在内存中如何存储的问题。除此之外,不同位数的处理器(如16位、32位、64位)其寄存器的宽度也不同,因此也会涉及到字节顺序的问题。不同的处理器或者不 1.什么是大小端 字节序 在我们的电脑中,系统在会将多个字节的数据存储在一片连续的地址上,而将数据的各个字节从这片空间的高地址位开始存储还是从低地址位开始存储就决定了系统的存储 字节序 1.1 小端 字节序 在32位的操作系统中,int 类型的变量占4个字节,我们利用这样一个变量来说明什么是大端 字节序 什么是小端 字节序 ; 定义一个变量并赋于初值    在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题。这是就可能用到htons(), ntohl(), ntohs(),htons()这4个函数。 网络字节顺序与本地字节顺序之间的 转换 函数:       htonl()--"Host to Network Long"