查看原文
其他

【C进阶】这种地方别再强制类型转化了,来告诉你个小技巧!

bug菌 最后一个bug 2021-01-31


1、聊一聊

    今天跟大家推荐的这首歌曲最早应该是在作者初中的时候听到的,每次心情不好的时候听到总会觉得一切又是这么的美好,所以歌曲确实能改善一个人的心情!

    本篇文章主要是跟大家介绍一个C语言的小技巧,难度不大,不过得记得在实际的项目中用起来!

2、谈谈知识转化率

    所谓知识转化率体现的是一种知识提升生产力的能力,比如说在高中的时候,一些同学购买了大量的学习资料,里面有各种题目的具体解决办法和解题技巧,这些同学也有认真的练习和学习了这些技巧,可是当老师换了一下题目的形式,这些同学就为此抓狂了,这样知识转化率是很低的。


     同样,步入社会其实每天也是在不断的解决和处理问题(比如修护bug),大家在各自的领域通过书籍、教程或者公司的代码、技术文档得到了大量的开发技巧和思路,然而部分小伙伴一旦遇到具体问题还是慌了神,所以大家要通过练习总结抽象来培养问题解决办法关联的能力。
    下面作者开始讲小技巧了,大家一定要记得问题与方法关联起来!

3、想要解决的问题

    本篇文章主要解决的问题是C语言整形与浮点型转化过程中的精度损失从而容易产生bug,对于浮点和整形的相关内容大家可以参考下面两篇文章<【重磅】“整形数”还真没那么简单(C语言版)>、<【典藏】别怪"浮点数"太坑(C语言版本)>,这里就不再赘叙了。


1

整形与浮点转化的精度损失
参考小程序:
1#include <stdio.h>
2#include <stdlib.h>
3
4/*************************************
5 * Fuction:精度损失 
6 * Author : (公众号:最后一个bug) 
7 ************************************/
 
8 int main(int argc, char *argv[]) {
9    float fVal1 = 0;
10    int   sVal1 = 60012502;
11    int   sVal2 = 60012501;
12
13    printf("sizeof(int) = %d\n",sizeof(int));
14    printf("sizeof(float) = %d\n",sizeof(float));
15
16    fVal1 = (float) sVal1;
17    printf("fVal1 = %f\n",fVal1);
18
19    fVal1 = (float) sVal2;
20    printf("fVal1 = %f\n",fVal1);
21
22
23    sVal1 = (int) fVal1;
24    printf("sVal1 = %d\n",sVal1);
25
26    sVal2 = (int) fVal1;
27    printf("sVal2 = %d\n",sVal2);
28
29    if(sVal1 == sVal2)
30    {
31        printf("sVal1 == sVal2\n");     
32    }
33    else 
34    {
35        printf("sVal1 != sVal2\n"); 
36    }
37
38    printf("公众号:最后一个bug\n"); 
39
40    return 0;
41}
运行结果:

分析一下:
  • 我只想说真的牛,转来转去竟然使得两个不一样的数相等了,所以大家在平时的项目中进行强制类型转化可要慎重考虑下了。

  • 其发生该情况的主要原因是由于4字节float类型变量不能完全覆盖int类型的所有类型数值,所以在进行强制类型转化的过程中会产生精度上的损失,这样大家稍不留意就会有一些不必要的麻烦。

2

没必要强制转化的地方

    大家对数据进行强制类型转化很大部分都是为了方便计算,比如通过AD采样获得的数据都是整形的数据,通过比例因子处理以后成为了浮点数据,然后通过浮点进行计算实在是再方便不过了。

    对于上面这种强制类型转化情景是值得考虑的,可是对于下面的场景就没有必要了,反而容易出现问题。



    对于上面这种处理情景,估计很多小伙伴就直接通过整形接受到数据,然后强制类型转化为了浮点类型进行保存,当需要使用到该参数的时候,又通过把该浮点存储保存的值强制类型转化为整形,最后再进行整形参数的相关处理。

    经过这样整形-->浮点-->整形的处理,如果你拿着最后的整形进行数据判断,就很大概率上会存在类似于前面的Demo程序那样存在bug。

   那么有一些小伙伴该问了,为什么不用整形存储呢 ? 用整形存储不就没这个问题了吗 ?当你真正进入工作岗位,可能某一天一个同事离职,把一堆摊子丢给你了,然而里面的数据结构等等处理,不是不想动,而是不敢动,往往新接手的代码把控能力有有限,可能不小心的参数配置都会导致程序奔溃,还真不敢大动作?所以还是技巧性的填好坑,等待下一次重构的机会。

3

处理小技巧

    其实对于内存上所保存的数据是没有具体类型的,所谓数据类型仅仅只是以怎样一种方式进行数据的访问罢了,我们通过指针来进行访问就很好的说明了这个问题,例如int *ptr,表示ptr所指向的地址以int类型进行访问,如果把ptr改为float类型即float *ptr,其便以float类型来访问地址所对应的内存。

    既然像第二小节说的float类型的参数保存区无法修改了,那么我们就想办法直接绕过float类型来进行数据访问。

参考demo:
1#include <stdio.h>
2#include <stdlib.h>
3
4/*************************************
5 * Fuction: 两个宏--可以继续扩展 
6 * Author : (公众号:最后一个bug) 
7 ************************************/
 
8#define SET_INT_VAL(addr,val)  *((int*)(&addr)) = val
9#define SET_GET_VAL(addr)      *((int*)(&addr))
10
11/*************************************
12 * Fuction: 改善小技巧 
13 * Author : (公众号:最后一个bug) 
14 ************************************/
 
15 int main(int argc, char *argv[]) {
16    float fVal1 = 0;
17    int   sVal1 = 60012502;
18
19    printf("sizeof(int)   = %d\n",sizeof(int));
20    printf("sizeof(float) = %d\n",sizeof(float));
21
22    //接受整形数据到float数据内存 
23    SET_INT_VAL(fVal1,sVal1); 
24
25    //float数据内存获得整形数据 
26    printf("SET_GET_VAL(fVal1) = %d\n",SET_GET_VAL(fVal1)); 
27    printf("            sVal1  = %d\n",sVal1); 
28
29    printf("公众号:最后一个bug\n"); 
30
31    return 0;
32}
运行结果:

分析一下:
  • Demo中通过两个宏进行地址的类型处理转化,对float内存进行直接访问来保存整形数据,同样后面通过地址的直接访问来获得整形数据,这样就可以绕开float类型的处理问题。

  • 同时大家也要注意其本地类型占用字节大小(float)需>=所要转化的类型占用字节大小(int),否则会造成数据的篡改,这样就会造成更多麻烦。

  • 其实这里仅仅只用float进行存储参数的设计,原本就存在设计上的缺陷,可以通过共联体,或者无类型内存设计方案进行处理,这样就不会出现类似的问题了。

 

4、最后小结

    今天主要是跟大家分享了一个小案例,大家好好理解下,C语言虽然语法不多,不过想要写出好的C代码并非易事,需要多练多悟!作者也会慢慢回顾这些年的编程经验并分享给大家。

    好了,这里是公众号:“最后一个bug”,一个为大家打造的技术知识提升基地。同时非常感谢各位小伙伴的支持,我们下期精彩见!

推荐好文  点击蓝色字体即可跳转

C语言数值常量的“那些事”(细节分析)

【C进阶】嵌入式开发中"移位操作"可要注意了!

goto关键字你不知道的"那些事"(C语言提升)

【重磅】剖析MCU的IAP升级软件设计(设计思路篇)

【贼稳定】" IO口模拟串口 "之优化半周期法

【典藏】自制小型GUI界面框架(设计思想篇)

【C进阶】听说用 “ 逗号表达式 ” 仅仅为了秀技?

【经典】把脉printf中的C进阶技巧

【进阶】嵌入式编程技法之"数据驱动编程"

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存