写程序就是培养一种好习惯,有些及其容易犯的bug隐蔽的很深,需要特别的注意。
1. 赋值。a=0大家都知道0==a更好,所以任何时候只要有可能,就写成0==a
2. 缓存处理。有时候为了效率,把接受到的数据缓存起来然后一次性处理。这样的话,到了程序退出的时候千万别忘记了缓存中可能还有未处理的内容。
3. 如果C++程序出现随机错误,那几乎可以肯定的时候内存访问问题。我喜欢get的时候如果不存在则创建,可是这是一种不太安全的行为,如果某个时候别人就是要get数据,则会出大问题。比如A[I]不存在,这时候别人访问A[I],你自行创建了一个,则会造成存在的假象,更加恐怖的是,如果接下来别人要访问了,那么访问的就是未经初始化的数据,或者,你如果做了初始化,那就访问原始数据,那么每次都得到相同的数据。所以,如果总是看到相同的数据应该小心。
4.单元测试一定要做。很大程度上能节省debug的时间。
5. 如果两个数据结构比较相似,但是你知道不应该放在一个逻辑里面,于是就像拷贝出来,然后改之,那么我提醒你,即使你抄一遍也比拷贝好。因为既然相似,那么改动的时候很可能就漏改了一些地方,这就是最深最深的bug隐藏的根源。
6. 在一个很长的公式中,我写道
r=A-B
-C+C
*E-
LOG(X)
后来为了方便调试就像在前面加上变量,于是
r = A-B
r -= C+C
r *= E
r -=LOG(X)
这样潜在的改变了语义,因为右面的公式本来应该是顺序执行的减法,但是现在变成了加法了。
下面的来自toplanguage
1. Thread safe
我要用到一个库,这个库被声称为只能Initialize一次,其手法是在库中定义一个bool全局变量来表示是否被初始化,如
if(bAlreadyInit)
{
return ERROR_ALREADYINITIALIZED;
}
但是这个变量没有用临界区等保护手段,因此两个线程可能同时成功越过上面那个判断语句,导致这个库被初始化两次。
2. Check input parameter
具有依赖关系的两个API,第二个API的参数需要用到第一个API的返回值,诸如GetNextXXX(HANDLE handle)的参数需要用到
GetFirstXXX()的返回值。如果用户没有用GetFirstXXX()而随意给GetNextXXX()传递一个参数,有可能造成
Access Violation。我曾经就此问题和代码编写者提出意见,但对方却说只能这样,他们不希望用户这么去用他们的这个GetNextXXX
()。我觉得这种情况应该返回一个error code而不是让程序当掉,好像曾经在Bjarne的某个slide上看到也有这么一说的。
3. Logarithm
在Win CE上的程序调用了Log这个函数,用来动态地生成一些索引值。按编写者的预期,Log8应该等于3,确实在他的机子上跑时是等于3,但在我
的ARM板上等于2,还有后面的Log64等都与预期不相符。对方在USA,经过几次交流之后他决定不用Log这个函数,用位运算把整个类重写一遍。
4. delete void*
程序中某些地方需要将指针p转成void*类型,在释放时直接delete p,这导致了p所指向对象的析构函数没被调用,资源泄漏。
5. destruct order
在一个测试函数中有这么几行代码:
void fun()
{
...
B b;
A a;
b.Insert(&a);
...
}
其中b会开一个线程,线程中用到a的东西,在b析构时,线程会被停掉。这个函数在退出时会有access violation,原因是一般编译器构造和
析构对象是按一定的顺序的,上面的代码使得a先于b析构,而b创建的线程还在用a的资源。把a的声明放到b之上则可解决问题。当然,这是review测
试代码时发现的,产品代码中应该不会有人这么写吧。
6.out of handle
程序连续跑了几天之后停下来,debug窗口输出系统信息out of handle。大家都认为自己的代码里没有什么handle不释放,结果仔细检
查之后发现很多Create出来的handle是被释放了,但是Open的handle却没有被释放。更甚的是用一个局部变量去接收
CreateEvent()返回的值,而这个event在以后会用到,因此该函数内不会close这个handle,这样反复的Create和Open
单进程的Unix程序,用signal来处理子进程的退出SIGCLD,并将所持有的子进程相关的信息结构、资源清理掉。
进程的主体这是一个处理消息的阻塞循环,典型的Unix用法,表面上看来没有多线程的问题;但该程序在子进程个数超过200个的时候,却时不时会挂起,主进程根本无法处理子进程发送的任何消息,甚至SIGCLD也没有处理,造成一大堆僵尸进程,问题发生的情况是所有的子进程都在同一瞬间退出。
用调试工具查看和分析才发现(这个过程相当漫长, 服务器程序,问题重现比较耗时间),进程挂起在signal处理中的delete操作上。
Google+分析之后得出的结论是,delete不能在signal中调用,malloc/free也不行,假设主进程在malloc,这个时候发生信号中断,信号中用了free/delete,那么libc会自己锁住。
阅读(753) | 评论(1) | 转发(0) |