找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4365|回复: 20
打印 上一主题 下一主题
收起左侧

解决一个C语言程序分号“;”引起的bug

  [复制链接]
跳转到指定楼层
楼主
ID:282850 发表于 2022-9-13 23:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
曾想也标题党一次,写成《一个C程序分号“;”引起的血案》,笔者原本厌恶标题党,厌恶那些“xxxx了”、“XXXX震惊,不看后悔”、“XXXX,看这个就够了”。也就不在此恶心各位同好。
C程序的分号,是任何写过C程序的人都明白的,很重要,用的很多。但此文将要说明的是分号不正确的使用,导致一个隐藏的Bug。

一、发现错误
家用的太阳能热水器控制,执行电加热后,本来有一个LED灯闪烁,提示电加热是开启的。原来用了两年多,一直正常,前几天发现是启动电加热后,闪一次LED就熄灭。全套控制、接线都是自己一点点搞起来的,所以要解决这个故障,有了优势,但更有了苦命累积。一个同事二货在别墅顶上装了太阳能热水器,为了阴天有热水,另外加了一个电热水器,还要考虑阀门切换,考虑电热水器防雨、考虑启动或关闭总电源等等,反正一切由安装的说了算,不用费脑子。想想我布的多根网线、多点测温、两组太能能占据掉的位置,自己在斜的瓦屋顶上熔水管、焊接线,那么多辛苦都过去了,借肋中秋节放假而且一直下雨在家的时间,有必要把LED不指示的问题解决。

二、查找问题
记得LED指示灯与电加热分别各用一个IO口,也必须如此,才可以闪烁,不然加热时变成继电器反复通断。LED用9012PNP管驱动,且平时一直会微闪,这个一开始就如此,胡乱微闪就闪吧,大概分析了一下,如果用NPN管不会微闪,PNP如果调偏置电阻也可以使全灭,涉及小小的三极管复杂的功能,在此不跑主题,也不细说。
未连接系统,随意看了一下程序,程序应该没有动过,上月写两个发明专利时,为了测试几个数据,重新下载过一套改版的程序到Flash,后又恢复了。原因可能是在什么时候动过程序,导致出现Bug。程序看不出明显的错误,必须联机调试,监视一下变量。
真是累不动。还好SBW下载线制作过一根专用的,直接插上就OK,不用其它排列顺序的下载线换线位。搜索全部工程文件“电加热” “电热”,找到以下语句:
#define EHeat_off   P7OUT |=  BIT0; EHisON=0  //电加热off
#define EHeat_on    P7OUT &=~ BIT0; EHisON=1  //电加热on
聚焦到EHeat_off语句,再搜EHeat_off,只有3处有过:
1、              BASICTIMER_VECTOR定时器中断250ms一次,快结束时启动一次测温,在测温程序中,结果出来后,用了以下:
  /*-----------停止电加热-----------*/
if(UperT > EleHeatTempe*10)  EHeat_off;     //达到设置温度(45.0---450),关闭电加热。
2、              第二处是状态机控制的菜单程序中,当Cancel时,关闭电加热:
if(Menu_sel==3)   EHeat_off;
另外一处就是define处的定义了。

三、解决问题
程序高手估计已经发现问题了,笔者愚钝。启动电加热后,中断并监视P7口,P70确实是0,刚好这两周,家里主空开被我拆了调整空开位准备在家门口接充电桩,用钳表卡16平总进户线电流5A多,关闭电加热时0.3A。证明加热正常。仅仅是指示灯出故障。再搜索全部工程,除了3处定义,只在__interrupt void BT_ISR(void)中出现过:
if(  EHisON) P4OUT ^=  BIT2;      //电加热指示灯闪
else  if((P4IN & BIT2) ==0 )   P4OUT |=  BIT2;
程序前强加一个EHisON=1; 指示灯会闪。说明PNP管、LED均正常。EHisON是一个标志电加热是打开状态的变量,原意是开关电热时,一并切换此标志是开或关。监视此变量总是为0。
回到if(UperT > EleHeatTempe*10)  EHeat_off;终于发现问题。这个语句相当于:
if(UperT > EleHeatTempe*10)  P7OUT |=  BIT0; EHisON=0;
很明显,if执行了“P7OUT |=  BIT0;”,而“EHisON=0;”不论if如何都会执行,解决办法自然是加{}。当然还有另一个解决办法,不用加{},看大家的兴趣,我在后边回复中说明。

我一般写define时,习惯是最后不加“;”而在执行代码中加,这样看上去比较统一。此次定义是P7OUT |=  BIT0和EHisON=0两个语句,故在中间顺手加了一个“;”。又一个习惯是简单的if喜欢一行写完,不加{}。甚至长一点的if都喜欢一行写完,以让小小的屏幕好容纳上万行代码。各种习惯、巧合,导至这个Bug正常运行了2年时间。今天回头找最老版的程序查看,这个错误一直在,不明白为什么原来可以正常闪烁,难道编译器根据option设置修正过此错误?如果有明白的大神,请不吝赐教,先谢谢了。主控是MSP430F4152,但IDE我不说了(IXX6),涉及其它不必要的麻烦。
另外,我不喜欢那些上传整套程序的,一般也无心看,除非是自己手上有的小模块,而又没有玩过的,针对此模块的驱动可以看看。也更喜欢关注思路、方法、原理。所以也不上传整套程序,毕竟各种场合的应用不一样、解决方案各不同,拙作也就不伤大家眼了。

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶3 踩
回复

使用道具 举报

沙发
ID:614312 发表于 2022-9-14 12:56 | 只看该作者
学C语言的经典 坑了。
回复

使用道具 举报

板凳
ID:262 发表于 2022-9-15 20:53 | 只看该作者
对初学者很有用,当年好像遇到过这情况
回复

使用道具 举报

地板
ID:1046043 发表于 2022-9-27 16:29 | 只看该作者
弄了个开发板,准备开始学单片机啦,加油
回复

使用道具 举报

5#
ID:151348 发表于 2022-10-9 09:30 | 只看该作者
别提了,我也有你提到的编程坏习惯,语句写成一行,又不加{},开始时死活找不出bug,以后真的要注意了
回复

使用道具 举报

6#
ID:310441 发表于 2022-10-11 07:20 来自手机 | 只看该作者
后面你一定会养成if的内容加花括号的习惯的
回复

使用道具 举报

7#
ID:260656 发表于 2022-10-16 17:31 | 只看该作者
{}太重要了,被坑过
回复

使用道具 举报

8#
ID:1032507 发表于 2022-10-28 10:10 | 只看该作者
受教了,给大家也提了个醒,以后会注意这个问题,养成良好的编程习惯。
回复

使用道具 举报

9#
ID:1049806 发表于 2022-11-7 15:30 | 只看该作者
确实细节很重要
回复

使用道具 举报

10#
ID:77589 发表于 2022-11-11 14:29 | 只看该作者
这个根本就不是";"号的号题!!!!
在定义多条语句宏的时候,至少需要用{}把语句框起来!
#define EHeat_off   {P7OUT |=  BIT0; EHisON=0;}  //电加热off
#define EHeat_on    {P7OUT &=~ BIT0; EHisON=1;}  //电加热on
如果想更加保险一点应用下面格式写更好
//电加热off
#define EHeat_off   do {P7OUT |=  BIT0; EHisON=0;} while(0);
//电加热on
#define EHeat_on   do {P7OUT &=~ BIT0; EHisON=1;} while(0);
为什么这样写,网上有大佬分析过。


回复

使用道具 举报

11#
ID:142059 发表于 2022-11-11 23:58 来自手机 | 只看该作者
使用宏替换多行代码时,在调用养成加大括号后再调用的习惯
回复

使用道具 举报

12#
ID:142059 发表于 2022-11-11 23:59 来自手机 | 只看该作者
Longan.Wang 发表于 2022-11-11 14:29
这个根本就不是";"号的号题!!!!
在定义多条语句宏的时候,至少需要用{}把语句框起来!
#define EHea ...

对的,用do来概括多行宏,根本不是分号问题,给你一个赞
回复

使用道具 举报

13#
ID:282850 发表于 2022-11-12 01:19 | 只看该作者
Longan.Wang 发表于 2022-11-11 14:29
这个根本就不是";"号的号题!!!!
在定义多条语句宏的时候,至少需要用{}把语句框起来!
#define EHea ...

感谢回复,我的解决办法就是这样:
#define EHeat_off   {P7OUT |=  BIT0; EHisON=0;}  //电加热off
#define EHeat_on    {P7OUT &=~ BIT0; EHisON=1;}  //电加热on
用do while应该有它的好处,大佬毕竟是大佬。
基于现在屏幕大部分时候是横向的,特别是对于我用笔记本来说。更喜欢把一件事写在一行,好象看着更简洁,好比:
unsigned char aa,bb,cc;
至于"定义多条语句宏的时候,至少需要用{}把语句框起来",这有点绝对了。
实际上:
#define EHeat_off    P7OUT |=   BIT0 ,EHisON=0     //电加热off
#define EHeat_on    P7OUT &=~ BIT0, EHisON=1  //电加热on
上边语句完全可以实现功能,且最后是没有分号的,以方便按习惯在调用时后边再加分号,如:
if(...)  EHeat_off;
上边实际上是“,”的用法,好比在写if时为节约行数,if后同时要做的事一并写了,不用{}。
如:if(a>b) c=a,a=b,b=c;    //交换顺序
毕竟是业余玩,不是按代码行数拿薪水,我个人更喜欢上边这种写法,当然,坏处是出现了我本主题的毛病,另外也不排除在上边if中随手把“,”写成分号导致隐藏的错误。
回复

使用道具 举报

14#
ID:879348 发表于 2022-11-12 08:31 | 只看该作者
一般不是数值的话,我都是搞成函数的形式,而不是#define
回复

使用道具 举报

15#
ID:507641 发表于 2022-11-15 15:56 | 只看该作者
Longan.Wang 发表于 2022-11-11 14:29
这个根本就不是";"号的号题!!!!
在定义多条语句宏的时候,至少需要用{}把语句框起来!
#define EHea ...

为什么这样写,网上有大佬分析过   请给连接想学下。谢谢
回复

使用道具 举报

16#
ID:1010910 发表于 2022-11-27 20:37 | 只看该作者
B站上有c 语言学习视频
回复

使用道具 举报

17#
ID:408539 发表于 2023-1-11 22:17 | 只看该作者
语句短我一般加,号不用{}
回复

使用道具 举报

18#
ID:282850 发表于 2023-2-15 22:44 | 只看该作者
明记冷气 发表于 2023-1-11 22:17
语句短我一般加,号不用{}

谢谢,还是找到一个知音了。
回复

使用道具 举报

19#
ID:399179 发表于 2023-4-11 20:20 来自手机 | 只看该作者
以后学C语要细心了!
回复

使用道具 举报

20#
ID:1073883 发表于 2023-4-24 21:00 | 只看该作者
答主讲的非常仔细
回复

使用道具 举报

21#
ID:1130105 发表于 2024-8-3 13:42 | 只看该作者
好多次了,IFELSE后跟一条语句,不加{};后来客户提要求,加了之后出BUG,头疼了半个多钟头
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表