总述:
字节间 | 字节内 | |
大端 | 低地址的数据放在高字节 | 位域:先从低地址对应数据字节的高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<stdio.h>
#include<Winsock2.h>
#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<stdio.h>
#include<string.h>
#define LOCAL_ENDIAN 1 /* 0为大端 1 为小端,根据处理器的类型赋值*/
typedef struct struct_test
{
#if 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, 现将数据进行翻转看,然后从底地址数据开始读: | ||||
可以看出小端是从底地址的字节的底bit位开始分配。
2.2在大端处理器运行看内存(#define LOCAL_ENDIAN 0):
地址 | 0X004AFC14 | 0X004AFC15 | 0X004AFC16 | 0X004AFC17 |
数据十六进制 | E4 | DB | B9 | D7 |
数据二进制 | 11100100 | 11011011 | 10111001 | 11010111 |
跨字节的部分需要特别注意: | ||||
三.对于枚举类型与联合体类型的处理。 | ||||
1.对于枚举类型按照unsigned int进行大小端转化就可以。 | ||||
2.对于联合体按照联合体中长度最长的数据类型以及长度进行转化就行。 |