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

一、字节序,为字节的顺序,就是大于一个字节类型的数据在内测中的存放循序,一个字节的数据

当然就没有顺序可言了。

二、大端序与小端序

字节序分为两类: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。在一个数组中arr[4]中,arr[0]是低地址端,arr[3]是高地址端。

三、网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次是8~15bit,然后是16~23 bit,

最后是24~31 bit,这种传输次序称作大端序字节,友谊TCP/IP首部中所有的二进制整数在网络中传输是都要

以这种次序,因此他又称作网络字节序,比如,以太网头部中2字节的“一台网帧类型”,标识后面数据的类型。

UDP/TCP/IP协议规定:把接收到的的第一个字节当做高位字节来看待,这就要求发送端发送的第一个字节是

高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中起始地址(低地址)对应的那个字节,

也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节(即:高位字节存放在

低地址处);

由此可见,多字节数值在发送之前,在内存中应该是以大端序存放的;所以说,网络字节序是大端字节序;比

如我们经过网络发送整形数值0x12345678时,在80X86平台中,它是以小端序存放的,在发送之前需要使用系

统提供的字节转换函数htonl()将其转换成为大端序存放的数值。对于ARP请求或应答的一台网帧类型来说,在

网络传输时,发送的顺序是0x08,0x06。

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

关于内存空间布局情况的说明:

----------------------- 最高内存地址 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)通过指针判断(返回真则为小断续,返回假则为大端序)

————————————————
版权声明:本文为CSDN博主「寂寂寂寂寂蝶丶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/SwordArcher/article/details/82417429
1 bool isLittleEndian()
2 {    
3 unsigned int i = 0x12345678;    
4 unsigned char* c = (unsigned char*)&i;    
5 return(*c == 0x78); //判断是否是低位字节存内存低地址  *c取得就是内存中存放i的低地址
6 }

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

 1 bool isLittleEndian()
 2 
 3 {
 4     union
 5 
 6     {
 7         int i;
 8 
 9         char c;
10 
11     }udata;
12     udata.i = 1;
13 
14     return(udata.c == 1);
15 }

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

1 #include <arpa/inet.h>
2 
3 bool isLittleEndian()
4 
5 {
6 
7     return (1 != htonl(1));
8 
9 }

七、大小端的转换

//短整型大小端互换#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位的网络字节序转换到主机字节序
————————————————
版权声明:本文为CSDN博主「寂寂寂寂寂蝶丶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/SwordArcher/article/details/82417429

猜你喜欢

转载自www.cnblogs.com/dzcheng/p/12904879.html
今日推荐