C语言将float拆分为4个hex传输与重组

问题引出

实际的编程应用中,特别是数据传输通信等场合,需要传输float等类型的数据,而常用的数据传输形式一般为hex格式字符串格式,通常我们会选用hex格式,更接近计算机的2进制,而这种传输方式就需要将float转换为hex格式了。

在计算机中,float占用4个字节,因此可以考虑将float拆分为4个hex格式的16进制数,完成数据传输后,接收方再将4个hex重组为float即可还原出原来的数据,这有点像数据的编码和解码的意味。

分步测试

float型的2进制形式

float的计算机中占用4个字节,具体是如何在计算机中存储的可以参考上一篇笔记:C语言打印数据的二进制格式-原理解析与编程实现,上次的int数据打印2进制的函数这里也可以用来测试,只需将参数类型改成float:

void printf_bin(float num)//注意这次这里的参数类型改成了float { int i, j, k; unsigned char *p = (unsigned char*)&num + 3; for (i = 0; i < 4; i++) //处理4个字节(32位) { j = *(p - i); //取每个字节的首地址 for (int k = 7; k >= 0; k--) //处理每个字节的8个位 { if (j & (1 << k)) printf('1'); else printf('0'); } printf(' '); } printf('\r\n'); }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

现在来测试一个float数据的2进制形式:

float a = 3.887;

//使用上次自己写的printf_bin函数打印一下float a的2进制形式
printf('查看一下float型a=%f的2进制形式:\r\n', a);
printf_bin(a);

//使用unsigned char来验证float的每一个字节
unsigned char *p1 = (unsigned char*)&a;  //获取a的首地址
unsigned char *p2 = (unsigned char*)&a + 1;//获取a的首地址的后一个字节地址
unsigned char *p3 = (unsigned char*)&a + 2;//获取a的首地址的后两个字节地址
unsigned char *p4 = (unsigned char*)&a + 3;//获取a的首地址的后三个字节地址
printf('\r\n查看a的每个字节的地址(16进制)与内容(10进制(+16进制)):\r\n');
printf('[a] p1:%x, %d(%x)\r\n', p1, *p1, *p1);//打印p1的地址与存储的字节内容
printf('[a] p2:%x, %d(%x)\r\n', p2, *p2, *p2);//打印p2的地址与存储的字节内容
printf('[a] p3:%x, %d(%x)\r\n', p3, *p3, *p3);//打印p3的地址与存储的字节内容
printf('[a] p4:%x, %d(%x)\r\n', p4, *p4, *p4);//打印p4的地址与存储的字节内容
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

输出:

查看一下float型a=3.887000的2进制形式: 01000000 01111000 11000100 10011100 查看a的每个字节的地址(16进制)与内容(10进制(+16进制)): [a] p1:5b5bf554, 156(9c) [a] p2:5b5bf555, 196(c4) [a] p3:5b5bf556, 120(78) [a] p4:5b5bf557, 64(40)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个输出结果实际上我们也无法直接看出拆分的到底对不对,上次测试int是对的,理论上也适用于float,因为2者都是4字节储存。

我们可以先继续拆分测试,最后重组看看是否可以还原数据。

数据拆分与重组

这里写了测试函数,先将float拆分为4个字节,保存在tbuf[0]~tbuf[3]中,并先打印查看是否正确。如果是在实际应用中,这时就可以将4个数据以hex的形式发送出去了。

然后将数据重组,这里直接使用tbuf[0]~tbuf[3]模拟接收方接收到的4个hex数据,将重组后的数据保存在res变量中,重组的方法也是根据float在计算机占4个字节,通过unsigned char指针依次为float的4个字节赋值即可

void test_float_to_4hex(float num)
{
unsigned char tbuf[4];
unsigned char *p = (unsigned char*)&num + 3;//指针p先指向float的最高字节
float res;//验证float拆分为4个字节后,重组为float的结果

//先打印一下传入的float的值
printf('\r\n传入的float的值:%f', num);

//获取对应的4个字节,从低位到高位,这时就可以用于hex格式的数据传输了
tbuf[0] = *(p-3);
tbuf[1] = *(p-2);
tbuf[2] = *(p-1);
tbuf[3] = *p;

//打印看一下
printf('\r\n查看float的每个字节内容(16进制):\r\n');
printf('%x,%x,%x,%x\r\n', tbuf[0], tbuf[1], tbuf[2], tbuf[3]);

//对拆分后的4个字节进行重组,模拟接收到hex后的数据还原过程
unsigned char *pp = (unsigned char*)&res;
pp[0] = tbuf[0];
pp[1] = tbuf[1];
pp[2] = tbuf[2];
pp[3] = tbuf[3];

printf('\r\n重组后的float的值:%f\r\n', res);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

测试一下该函数:

//模拟测试float转为4个16进制数进行数据传输的过程 test_float_to_4hex(a);

  • 1
  • 2
  • 1
  • 2

结果:

传入的float的值:3.887000
查看float的每个字节内容(16进制):
9c,c4,78,40

重组后的float的值:3.887000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

数据重组后可以还原,方法是可以的。

关于int型数据

int型数据与float一样都是占用4个字节,所以该方法也适用于将int转换为4个hex,只需修改float类型为int即可:

void test_int_to_4hex(int num) { unsigned char tbuf[4]; unsigned char *p = (unsigned char*)&num + 3;//指针p先指向int的最高字节 int res;//验证int拆分为4个字节后,重组为float的结果 //先打印一下传入的int的值 printf('\r\n传入的int的值:%d', num); //获取对应的4个字节,从低位到高位,这时就可以用于hex格式的数据传输了 tbuf[0] = *(p - 3); tbuf[1] = *(p - 2); tbuf[2] = *(p - 1); tbuf[3] = *p; //打印看一下 printf('\r\n查看int的每个字节内容(16进制):\r\n'); printf('%x,%x,%x,%x\r\n', tbuf[0], tbuf[1], tbuf[2], tbuf[3]); //对拆分后的4个字节进行重组,模拟接收到hex后的数据还原过程 unsigned char *pp = (unsigned char*)&res; pp[0] = tbuf[0]; pp[1] = tbuf[1]; pp[2] = tbuf[2]; pp[3] = tbuf[3]; printf('\r\n重组后的int的值:%d\r\n', res); }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

实测:

int b = -85776553;
test_int_to_4hex(b);
  • 1
  • 2
  • 1
  • 2

结果:

传入的int的值:-85776553 查看int的每个字节内容(16进制): 57,27,e3,fa 重组后的int的值:-85776553

  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

完整测试代码

下面是整个本文的整个测试程序:

#include <stdio.h>

void printf_bin(float num)//注意这次这里的参数类型改成了float
{
int i, j, k;
unsigned char *p = (unsigned char*)&num + 3;

for (i = 0; i < 4; i++) //处理4个字节(32位)
{
j = *(p - i); //取每个字节的首地址
for (int k = 7; k >= 0; k--) //处理每个字节的8个位
{
if (j & (1 << k))
printf('1');
else
printf('0');
}
printf(' ');
}
printf('\r\n');
}

void test_float_to_4hex(float num)
{
unsigned char tbuf[4];
unsigned char *p = (unsigned char*)&num + 3;//指针p先指向float的最高字节
float res;//验证float拆分为4个字节后,重组为float的结果

//先打印一下传入的float的值
printf('\r\n传入的float的值:%f', num);

//获取对应的4个字节,从低位到高位,这时就可以用于hex格式的数据传输了
tbuf[0] = *(p-3);
tbuf[1] = *(p-2);
tbuf[2] = *(p-1);
tbuf[3] = *p;

//打印看一下
printf('\r\n查看float的每个字节内容(16进制):\r\n');
printf('%x,%x,%x,%x\r\n', tbuf[0], tbuf[1], tbuf[2], tbuf[3]);

//对拆分后的4个字节进行重组,模拟接收到hex后的数据还原过程
unsigned char *pp = (unsigned char*)&res;
pp[0] = tbuf[0];
pp[1] = tbuf[1];
pp[2] = tbuf[2];
pp[3] = tbuf[3];

printf('\r\n重组后的float的值:%f\r\n', res);
}

void test_int_to_4hex(int num)
{
unsigned char tbuf[4];
unsigned char *p = (unsigned char*)&num + 3;//指针p先指向int的最高字节
int res;//验证int拆分为4个字节后,重组为float的结果

//先打印一下传入的int的值
printf('\r\n传入的int的值:%d', num);

//获取对应的4个字节,从低位到高位,这时就可以用于hex格式的数据传输了
tbuf[0] = *(p - 3);
tbuf[1] = *(p - 2);
tbuf[2] = *(p - 1);
tbuf[3] = *p;

//打印看一下
printf('\r\n查看int的每个字节内容(16进制):\r\n');
printf('%x,%x,%x,%x\r\n', tbuf[0], tbuf[1], tbuf[2], tbuf[3]);

//对拆分后的4个字节进行重组,模拟接收到hex后的数据还原过程
unsigned char *pp = (unsigned char*)&res;
pp[0] = tbuf[0];
pp[1] = tbuf[1];
pp[2] = tbuf[2];
pp[3] = tbuf[3];

printf('\r\n重组后的int的值:%d\r\n', res);
}

int main()
{
float a = 3.887;

//使用上次自己写的printf_bin函数打印一下float a的2进制形式
printf('查看一下float型a=%f的2进制形式:\r\n', a);
printf_bin(a);

//使用unsigned char来验证float的每一个字节
unsigned char *p1 = (unsigned char*)&a;  //获取a的首地址
unsigned char *p2 = (unsigned char*)&a + 1;//获取a的首地址的后一个字节地址
unsigned char *p3 = (unsigned char*)&a + 2;//获取a的首地址的后两个字节地址
unsigned char *p4 = (unsigned char*)&a + 3;//获取a的首地址的后三个字节地址
printf('\r\n查看a的每个字节的地址(16进制)与内容(10进制(+16进制)):\r\n');
printf('[a] p1:%x, %d(%x)\r\n', p1, *p1, *p1);//打印p1的地址与存储的字节内容
printf('[a] p2:%x, %d(%x)\r\n', p2, *p2, *p2);//打印p2的地址与存储的字节内容
printf('[a] p3:%x, %d(%x)\r\n', p3, *p3, *p3);//打印p3的地址与存储的字节内容
printf('[a] p4:%x, %d(%x)\r\n', p4, *p4, *p4);//打印p4的地址与存储的字节内容

//模拟测试float转为4个16进制数进行数据传输的过程
test_float_to_4hex(a);

//测试int型的转换
printf('\r\n该方法也有可以传输int,因为float和int在计算机中都是占4个字节,测试如下:\r\n');
int b = -85776553;
test_int_to_4hex(b);

return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109

运行结果:

查看一下float型a=3.887000的2进制形式: 01000000 01111000 11000100 10011100 查看a的每个字节的地址(16进制)与内容(10进制(+16进制)): [a] p1:d750f694, 156(9c) [a] p2:d750f695, 196(c4) [a] p3:d750f696, 120(78) [a] p4:d750f697, 64(40) 传入的float的值:3.887000 查看float的每个字节内容(16进制): 9c,c4,78,40 重组后的float的值:3.887000 该方法也有可以传输int,因为float和int在计算机中都是占4个字节,测试如下: 传入的int的值:-85776553 查看int的每个字节内容(16进制): 57,27,e3,fa 重组后的int的值:-85776553 请按任意键继续. . .

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
(0)

相关推荐