大小端转换
1 概念
大小端是设备存储数据的方式,分为大端存储
和小端存储
。
- 大端存储是指数据的高字节存放在低地址处,低位字节放在高地址。
- 小端存储与之相反,高字节存放在高地址,低位字节存放在低地址。
举个例子
int a = 0x12345678;
变量a
中的数据,0x1
为高字节数据,0x8
为低字节数据,0x12345678数据从高位到低位依次为0x1、0x2、0x3、0x4、0x5、0x6、0x7、0x8。一个int
类型的数据占4个字节,每个字节占1个内存地址,于是变量a
在内存中占用了4个地址,假定4个地址分别为0x01、0x02、0x03、0x04。
情况1:小端存储
*((char*)0x01) = 0x78;
*((char*)0x02) = 0x56;
*((char*)0x03) = 0x34;
*((char*)0x04) = 0x12;
情况2:大端存储
*((char*)0x01) = 0x12;
*((char*)0x02) = 0x34;
*((char*)0x03) = 0x56;
*((char*)0x04) = 0x78;
依据上述的结果,大端存储相当于将数据按字符串存储的方式正序
放置,小端存储则是将数据按字符串存储的方式倒序
放置。转成字符串数组的形式大概如下:
char str[4];
/* 大端存储 */
str[0] = 0x12, str[1] = 0x34, str[2] = 0x56, str[3] = 0x78;
/* 小端存储 */
str[0] = 0x78, str[1] = 0x56, str[2] = 0x34, str[3] = 0x12;
2 判定设备存储方式
一般情况下我们需要先确认当前设备是哪种存储模式,而判定的方式很简单。定义一个int类型的数据并初始化1个大一些的值,之后分别打印单个地址中存放的数据,再根据大小端的定义人工判定。例程如下:
int a = 0x12345678;
char* pt = (char* )&a;
printf("%p, %x\n", pt, *(pt));
printf("%p, %x\n", pt+1, *(pt+1));
printf("%p, %x\n", pt+2, *(pt+2));
printf("%p, %x\n", pt+3, *(pt+3));
目前市面上大部分硬件芯片都是小端存储。
3 适用场景
从大小端的概念上看,如果数据是单字节,那么就没有大小端的问题,但如果传输的数据是多字节类型的时候就要考虑大小端情况。大部分情况下是设备与云端数据交互时要处理大小端,以及上下位机交互数据时,总得来说只有在数据交互通信时才需要考虑大小端转换。
4 大小端转换
建议使用共用体,如下所示:
/* 将1个字变量拆分成4个字节,以此可以用来处理大小端问题 */
typedef union _word_split {
uint32_t word;
uint8_t byte[4];
} WORD_SPLIT_u;
WORD_SPLIT_u zero_time_u = {.word = 1641225600};
printf("%p, %x\n", byte, byte[0]);
printf("%p, %x\n", byte+1, byte[1]);
printf("%p, %x\n", byte+2, byte[2]);
printf("%p, %x\n", byte+3, byte[3]);
当我们将整型数据赋值到word
成员中,那么byte
成员就是单字节的拆分。