大小端模式转换

嗨喽,大家好,我是程序猿老王,程序老王就是我。

今天给大家讲一讲工作中经常遇到的大小端模式转换问题。

首先先来了解一下为什么会存在大小端模式转换

是因为在计算机中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8 bit。但是在C 语言中除了 8 bit 的char之外,还有 16 bit 的 short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型 x ,在内存中的地址为 0x0010,x 的值为0x1122,那么0x11位高字节,0x22位低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。

  • 大小端定义

不同机器内部对变量的字节存储顺序不同,有的采用大端模式(big-endian),有的采用小端模式(little-endian)。

大端模式是指高字节数据存放在低地址处,低字节数据放在高地址处。

小端模式是指低字节数据存放在低地址处,高字节数据放在高地址处。

下面给大家看一个示例就知道了什么是大小端了:

0x12345678在小端模式内存中的表示形式:


 内存 低地址 -----------------> 高地址
       0x78 | 0x56 | 0x34 | 0x12 
     低字节 -----------------> 高字节

0x12345678在大端模式内存中的表示形式:

 内存 低地址 -----------------> 高地址
     0x12 | 0x34 | 0x56 | 0x78
     高字节 -----------------> 低字节
  • 大小端模式转换方法

在网络上传输数据时,由于数据传输的两端可能对应不同的硬件平台,采用的存储字节顺序也可能不一致,因此 TCP/IP 协议规定了在网络上必须采用网络字节顺序(也就是大端模式) 。

通过对大小端的存储原理分析可发现,对于 char 型数据,由于其只占一个字节,所以不存在这个问题,这也是一般情况下把数据缓冲区定义成 char 类型 的原因之一。对于 IP 地址、端口号等非 char 型数据,必须在数据发送到网络上之前将其转换成大端模式,在接收到数据之后再将其转换成符合接收端主机的存储模式

Linux 系统为大小端模式的转换提供了 4 个函数,函数原型:


#include <arpa/inet.h> 
 
uint32_t htonl(uint32_t hostlong); 
uint16_t htons(uint16_t hostshort); 
uint32_t ntohl(uint32_t netlong); 
uint16_t ntohs(uint16_t netshort);

htonl 表示 host to network long,用于将主机 unsigned int 型数据转换成网络字节顺序; 
htons 表示 host to network short,用于将主机 unsigned short 型数据转换成网络字节顺序; 
ntohl、ntohs 的功能分别与 htonl、htons 相反。
  • 最后再给大家展示一下模拟实现大小端转换函数


typedef unsigned short int uint16;
typedef unsigned long int uint32;
 
/*短整型大小端互换*/
#define BigLittleSwap16(A)  ((((uint16)(A) & 0xff00) >> 8) | \
                            (((uint16)(A) & 0x00ff) << 8))
/*长整型大小端互换*/
#define BigLittleSwap32(A)  ((((uint32)(A) & 0xff000000) >> 24) | \
                            (((uint32)(A) & 0x00ff0000) >> 8) | \
                            (((uint32)(A) & 0x0000ff00) << 8) | \
                            (((uint32)(A) & 0x000000ff) << 24))

/*本机大端返回1,小端返回0*/
int checkCPUendian(void)
{
   union{
      unsigned long int i;
      unsigned char s[4];
   }c;

   c.i = 0x12345678;
   return (0x12 == c.s[0]);
}
 
/*模拟htonl函数,本机字节序转网络字节序*/
unsigned long int t_htonl(unsigned long int h)
{
       /*若本机为大端,与网络字节序同,直接返回*/
       /*若本机为小端,转换成大端再返回*/
       return checkCPUendian() ? h : BigLittleSwap32(h);
}
 
/*模拟ntohl函数,网络字节序转本机字节序*/
unsigned long int t_ntohl(unsigned long int n)
{
       /*若本机为大端,与网络字节序同,直接返回*/
       /*若本机为小端,网络数据转换成小端再返回*/
       return checkCPUendian() ? n : BigLittleSwap32(n);
}
 
/*模拟htons函数,本机字节序转网络字节序*/
unsigned short int t_htons(unsigned short int h)
{
       /*若本机为大端,与网络字节序同,直接返回*/
       /*若本机为小端,转换成大端再返回*/
       return checkCPUendian() ? h : BigLittleSwap16(h);
}
 
/*模拟ntohs函数,网络字节序转本机字节序*/
unsigned short int t_ntohs(unsigned short int n)
{
       /*若本机为大端,与网络字节序同,直接返回*/
       /*若本机为小端,网络数据转换成小端再返回*/
       return checkCPUendian() ? n : BigLittleSwap16(n);
}

-END-


关于更多嵌入式C语言、FreeRTOS、RT-Thread、Linux应用编程、linux驱动等相关知识,关注公众号【嵌入式Linux知识共享】,后续精彩内容及时收看了解。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值