第21条:把属于同一个类型的所有问题全都修复好
在某一个地方所发生的问题,也有可能出现在其他地方。之所以会这样,可能是因为开发者在这些地方都采用了相同的思路来编程,也有可能是因为使用了某个很容易遭到误用的API,或把错误的代码从一个地方复制到了其他很多地方。在许多较为成熟的开发环境或对安全要求很高的工作场合中,开发者并不会在修复了某一个问题之后就止步于此,而是会把属于同一类型的所有问题全都解决好,以防将来再出现类似的错误。
例如,你发现下面这条语句的除数可能会是0,并且已经将该问题解决了:
那么,接下来你还应该搜索整个代码,看看有没有其他地方也把totalWeight用作除数。你可以通过IDE或Unix的grep命令(参见第22条)来搜索:
做完了这一步之后,还应该考虑代码中有没有其他地方也会出现类似的除法问题。我们需要寻找并修复这些可能出错的地方。借助Unix的管道机制,可以轻松地实现这种搜索。下面这段命令,能够在四百万行C语言代码里面,把可能出现除法问题的地方找出来。
经过几次过滤之后,可疑的代码从5731行降为5045行,又降为2032行,最后只剩下1923行。这个代码量,使得我们可以在合理的时间范围之内,将其中的代码审视一遍。尽管过滤得并不是特别严谨(例如,sizeof有可能返回0、符号常量的值有可能是0),但毕竟可以使我们知道其中有哪些代码可能出现问题。这要比那种以工作量过大为借口而根本不去检查除法代码的做法好很多。
最后我们还要考虑的是:怎样在将来的工作中避免类似的错误。这可能需要我们对代码或者软件开发流程做一些调整。例如,如果程序总是由于误用某个API函数而出错,那么可以提供一个更为安全的版本,并且把原来的版本屏蔽掉。例如,在项目的全局include文件里面,可以写上这么一行代码:
根据上述定义,如果有人试图在程序里面使用gets函数(该函数很容易发生缓冲区溢出的问题),那么代码就无法编译或链接。如果程序是因为对类型错误的值进行处理而出错的,那么可以考虑进行更为严格的类型检查。此外,还可以通过运用静态分析技术或更为严谨的配置方案来找到许多程序错误(参见第51条)。
要点
- 修复了某一个错误之后,我们还需要寻找并解决其他相似的错误,并设法保证将来不会再出现此类错误。