大小端转换(一篇文章了解所有情况下的大小端转换方法)

总述:

字节间 字节内
大端 低地址的数据放在高字节 位域:先从低地址对应数据字节的高bit开始分配
小端 低地址的数据放在低字节 位域:先从低地址对应数据字节的低bit开始分配

注释:
对于大小端的几个结论:
1.char型变量和char型数组没有大小端的区分。
2.需要转化数据类型大于1个字节的数据类型:short,int,枚举,联合体等。
一.为什么要进行大小端转化?
简单说一下为什么要进行大小端转化:目前我接触到的,arm是小端,dsp是大端,电脑是小段,网络数据一般为大端,当arm需要和dsp通信的时候就会存在大小端转化的问题,每种芯片为什么选用大小端的由来就不赘述了。
二、字节间的大小端转化:
1,举例说明字节间的大小端:
内存中有如下一段数据(unsigned int 型),不同的大小端对这个段数据的理解不同,所以在我们实际工作中,当你查看到内存中的数据时候,首先要清楚这个处理器的大小端,才能理解这段数实际代表的值。

内存地址 0x00004000 0x00004001 0x00004002 0x00004003
0X12 0x34 0x56 0X78

如果是大端处理器:这段数代表:0x12345678;
如果是小段处理器:这段数代表:0x78563412 ;
内存中有如下一段数据(unsigned short 型):

内存地址 0x00004000 0x00004001
0X12 0x34

如果是大端处理器:这段数代表的值:0x1234;
如果是小段处理器:这段数代表的值:0x3412 ;
在两个不同大小端的处理器之间数据传输,数据再内存的存放顺序并没有变,不同大小端需要解读出通用的值,就需要进行大小端转化。
2,C语言大小端转化调用库函数:

htonl host to network ,l代表unsigned long型 也可以看成unsigned int
htons host to network ,s代表unsigned short型
ntohl network to host ,l代表unsigned long型 也可以看成unsigned int
ntohs network to host ,s代表unsigned short型

在网络传输中,一般要求是大端,而inter处理器是小端,network to host理解为大端转小端,而host to network 理解为小端转大端,本质上大端小端的转化算法是一致的,没有区别,
3.代码验证:

/*
*程序说明:大小端字节间转化的一个案例
*作者    :通信小卒
*时间    :2019.8.25
*/
#include
#include
#pragma comment(lib,"ws2_32.lib")

int main(void)
{
   unsigned int   ultest=0x12345678;
   unsigned short ustest=0x1234;

   printf("ultest小端:%x\n",ultest);
   printf("ustest小端:%x\n",ustest);
   ultest=htonl(ultest);/*unsigned int   大小端转化*/
   ustest=htons(ustest);/*unsigned short 大小端转化*/
   printf("ultest大端:%x\n",ultest);
   printf("ustest大端:%x\n",ustest);
   while(1);
}

在这里插入图片描述
三.对于字节内部位域大小端的理解与处理:
1.位域处理方法(特别注意):
大小端不同的芯片传递同一个结构体时,
第一步:需要将结构体中的位域部分根据大小端判断进行翻转(int型按照凑32bits翻转,short型凑16bits翻转 ,char型凑8bits翻转)。
第二步:接收到数据后,如果位域的数据类型是unsigned int型,就拼凑成32bits按照unsigned int进行大小端转化,如果是unsigned short 型就拼凑16bit按照unsigned short 进行大小端转化,如果是unsigned char型就不翻转。
2.案例说明位域的存储方式:

/*
*程序说明:位域大小端字节转化的一个案例
*作者    :通信小卒
*时间    :2019.8.25
*/
#include
#include

#define  LOCAL_ENDIAN   1  /* 0为大端  1 为小端,根据处理器的类型赋值*/

typedef  struct  struct_test
{
#ifdef  LOCAL_ENDIAN
  unsigned int  ula :7;
  unsigned int  ulb :2;
  unsigned int  ulc :5;
  unsigned int  uld :3;
  unsigned int  ule :6;
  unsigned int  ulf :4;
  unsigned int  ulg :5;
#else
  unsigned int  ulg :5;
  unsigned int  ulf :4;
  unsigned int  ule :6;
  unsigned int  uld :3;
  unsigned int  ulc :5;
  unsigned int  ulb :2;
  unsigned int  ula :7;
#endif 
}struct_test_bit;

int main(void)
{
    struct_test_bit  struct_test_endian;
	
	memset(&struct_test_endian,0,sizeof(struct_test_bit));

   struct_test_endian.ula=0x57;
	struct_test_endian.ulb=0x3;
	struct_test_endian.ulc=0x1c;
	struct_test_endian.uld=0x6;
    struct_test_endian.ule=0x2d;
	struct_test_endian.ulf=0x9;
	struct_test_endian.ulg=0x1c;

	printf("&struct_test_endian=%x\n",&struct_test_endian);
    while(1);
}

处理LOCAL_ENDIAN 不同,其他代码完全相同,在大小端不同的处理器上运行此代码,分析内存情况:

2.1在小端处理器下看内存(#define LOCAL_ENDIAN 1 ):

地址 0X004AFC14 0X004AFC15 0X004AFC16 0X004AFC17
数据十六进制 D7 B9 DB E4
数据二进制 11010111 10111001 11011011 11100100

跨字节的部分需要特别注意:
在这里插入图片描述
小端数据为:0x E4BDB9D7, 现将数据进行翻转看,然后从底地址数据开始读:
大小端转换(一篇文章了解所有情况下的大小端转换方法)_第1张图片

可以看出小端是从底地址的字节的底bit位开始分配。
2.2在大端处理器运行看内存(#define LOCAL_ENDIAN 0):

地址 0X004AFC14 0X004AFC15 0X004AFC16 0X004AFC17
数据十六进制 E4 DB B9 D7
数据二进制 11100100 11011011 10111001 11010111

跨字节的部分需要特别注意:
大小端转换(一篇文章了解所有情况下的大小端转换方法)_第2张图片
三.对于枚举类型与联合体类型的处理。
1.对于枚举类型按照unsigned int进行大小端转化就可以。
2.对于联合体按照联合体中长度最长的数据类型以及长度进行转化就行。

你可能感兴趣的:(C语言)