深入分析根据字节取数据

时间:2023-01-11 23:16:11

如何将一个数据包中的部分数据提取出来,并转化为有效数据,相信这是很多做网络、串口的同仁们遇到的问题,希望这篇文章能给你一些思路:)


如果我们想要提取特定字节数的数据,首先大家可能想到的是按位提取,通过移位操作的方法,我本科时候也是这么做的,但是这里有缺点:非常难懂,不容易修改。过了几天自己都不知道怎么编写的了,这里使用另外一种办法,巧妙地利用基本类型来取出数据。

一、利用typedef统一规范

废话少说,先统一规范:

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;

我们通过sizeof进行查看,发现无论在32位还是64位的编译器环境下:

u8=1(字节) u16=2(字节) u32=4 (字节)

这就非常巧妙了,无论什么环境,我们提前定义好的u8,u16,u32就像一个漏斗。程序员给u8说,我给你一个地址,你以这个地址起始取出1个字节的数据来,u8只能乖乖地去内存那里取出8位数据来。想取多(非法访问)也没有办法吖,尺寸所限!说到这里,你大概明白我想干什么了吧~


二、从一个简单的例子讲起

这里在一个C++的文件编写C程序,为什么要用C++呢,因为在C++兼容的C规范更加严格,不允许一些模糊的类型变换,此刻我们开始使用u8这个漏斗啦

  u32 x = 0x11223344;
u8 *c =(u8 *)&x;
printf("x1=%x\n",*(c));
printf("x2=%x\n",*(c+1));

这里以一个最简单的例子作为说明,假设我们定义一个4个字节的数据(当然你也可以理解为在内存中存储连续4个字节的数据),由于我们在X86架构下为小端模式,所以我们剧透下:低地址起始的一个字节为0x44,我们要做的就是取出这个数据来~
首先,我们需要定义一个指针,这个指针指向的地址为变量x的地址,能够访问的长度为1个字节,如下:

u8 *c =&x; //错误,C++下不通过,地址类型不匹配
u8 *c =(u8 *)&x; //正确

需要注意的是,虽然我们的想法是正确的,但是编译器是不会通过的,因为变量x是一个4字节的数据,我们使用一个u8的指针访问4字节变量的地址,左右类型不匹配,因此我们需要使用(u8 *)&x进行强制转换。得到的结果就是0x44。
如何直接访问呢?

printf("x1=%x\n",*(u8*)(&x));

1,取变量x的地址: (&x)
2,1个字节对应的指针:(u8*)(&x)
3,由于()的优先级比解引用”*”高:*(u8*)(&x))


三、提升难度版

如果我们想随心所欲的提取数据,该怎么办?这里同样以变量x为例,提取出变量x的低4个字节和高4个字节。

//使用中间变量,简单
u16 *d =(u16 *)&x;
printf("x1=%x\n",*(d));
printf("x2=%x\n",*(d+1));
//直接访问,复杂
printf("x1=%x\n",*(u16*)(&x));
printf("x2=%x\n",*((u16*)(&x)+1));

这里分析直接访问的代码,仍然按照刚才的办法进行分解:
1,取变量x的地址: (&x)
2,2个字节对应的指针:(u16*)(&x)
3,由于()的优先级比解引用”*”高:*(u16*)(&x))
4,如果继续提取,首先对地址+1,然后解引用:*( (u16*)(&x) +1 )


四、代码验证

#include <stdio.h>
#include <stdlib.h>
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;

int main()
{
printf("u8=%d,u16=%d,u32=%d\r\n",sizeof(u8),sizeof(u16),sizeof(u32));
u32 x = 0x11223344;
printf("---------取一个字节-----------\r\n");
u8 *c =(u8 *)&x;
printf("x1=%x\n",*(c));
printf("x2=%x\n",*(c+1));
printf("x3=%x\n",*(c+2));
printf("x4=%x\n",*(c+3));

printf("x1=%x\n",*(u8*)(&x));
printf("x2=%x\n",*((u8*)&(x)+1));
printf("x3=%x\n",*((u8*)&(x)+2));
printf("x4=%x\n",*((u8*)&(x)+3));

printf("---------取两个字节-----------\r\n");
u16 *d =(u16 *)&x;
printf("x1=%x\n",*(d));
printf("x2=%x\n",*(d+1));

printf("x1=%x\n",*(u16*)(&x));
printf("x2=%x\n",*((u16*)(&x)+1));

printf("---------取四个字节-----------\r\n");
//u32 *p =(u32*)&x;
u32 *p =&x;
printf("x1=%x\n",*(p));

printf("x1=%x\n",*(&x));
system("pause");
return 0;
}

截图:
u8=1,u16=2,u32=4
———取一个字节———–
x1=44
x2=33
x3=22
x4=11
x1=44
x2=33
x3=22
x4=11
———取两个字节———–
x1=3344
x2=1122
x1=3344
x2=1122
———取四个字节———–
x1=11223344
x1=11223344