写程序多年,创造了很多BUG,总结一下这些BUG的原因。
一、清零
这个原因很常见,特别是底层驱动代码,不可避免需要一些全局变量,对于这些全局变量的清零要注意。
一般可分为:
1.应该清零的地方没有清零。
或者是忘记清零,或者是误以为不需要清零。
2.不该清零的地方却清零了。
这会导致变量结果得不到累积。
函数越长,或是越想节省临时变量一个变量多次无关使用,逻辑混乱,越是容易出现这类错误。所以个人认为,函数设计准则:短小精悍,功能单一,尽量可重入。
二、内存释放
通常,成功申请一段内存后,假如忘记了释放,结果就是可用内存越来越少,最后程序完蛋。
一般而言,内存申请函数与内存释放函数是在一个函数内,必须要成对使用。这是一对“函数对”。类似的还有一些系统中的进出临界区函数对(FreeRTOS:taskENTER_CRITICAL()/taskEXIT_CRITICAL()),进出中断通知函数对(uCOS:OSIntEnter()/OSIntExit()),FATFS中的打开关闭文件函数对(f_open()/f_close())。
我想,或许IDE可以提供一种特殊的宏(或assert或其他东西)供程序员使用,做到代码预编译时,一旦发现没有成对使用自定义的函数对,就会提示警告。
假设此宏形式为__CHECK_PAIRS(p)
这时,自定义一对函数对:
void f_ENTER()
{
__CHECK_PAIRS(f_ENTER) ;
//DO SOMETHING
}
void f_EXIT()
{
__CHECK_PAIRS(f_EXIT) ;
//DO SOMETHING
}
那么,在一个函数中,我们使用了f_ENTER(),而没有f_EXIT(),IDE预编译时会通过__CHECK_PAIRS检测到此情况,发出警告提示。
即使这样不能完全检测出这些错误(比如函数中有循环使用此类函数),但依然能够提供不少帮助。(有些瞎扯了)
三、空值检测
创建一个对象,或者申请一段内存,都是有可能失败的。正常来说应该做一个判断,保证程序正常处理。
char *p=malloc(10);
if(NULL == p)
{
//DO SOMETHING
}
四、判断相等
1.“=”与“==”误用。在C中,一不小心把“=”当做“==”。很多IDE都会提示warning,不过有些IDE没有任何提示。
2.浮点数。在C中,浮点数判断相等最好不要使用“==”,可以这么做:两数相减,判断结果的绝对值是否小于某个很小的数。
3.字符串,引用类型的比较。在一些高级语言如C#、java中,“==”与equals方法不是一回事。“==”是判断引用的地址是否一致,也就是两者是否是同一个对象;equals则是比较“内容”。
五、volatile
编译器帮你优化程序,但可能修改了你的原本逻辑思路。
使用volatile修饰的变量,意味着“每次都要去原始地址读,别偷懒去使用上次的备份”。该用volatile的地方必须要用,否则你有机会发现:某个变量明明应该变了啊怎么没有变化呢?
多任务包括中断函数共享的标志变量,硬件寄存器映射的变量,是volatile的使用场合。