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

您所在的位置:网站首页 大端小端字节序 字节序、大端序与小端序及其相关转换

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

2024-07-15 05:32| 来源: 网络整理| 查看: 265

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

 

一、字节序定义

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

二、大端序与小端序

字节序分为两类: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 bool isLittleEndian() { return (1 != htonl(1)); }

七、大小端的转换

(1)自定义函数

//短整型大小端互换 #define BigLittleSwap16(A) ((((uint16_t)(A) & 0xff00) >> 8 ) | \\ (((uint16_t)(A) & 0x00ff) > 24) | \\ (((uint32_t)(A) & 0x00ff0000) >> 8 ) | \\ (((uint32_t)(A) & 0x0000ff00) 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; }

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3