用于代码检查的错误列表
当你的代码出现错误而你又不知道有什么问题的时候,不妨看看这个错误列表.
代码检查过程的一个重要部分就是对照一份错误列表,来检查程序是否存在常见错误。
该错误列表很大程度上是独立于编程语言的,也就是说,大多数错误都可能出现在任意语言编写的程序中。
数据引用错误
-
是否有引用的变量未赋值或未初始化? 这可能是最常见的编程错误,在各种环境中都可能发生。在引用每个数据项(如变量、数组元素、结构中的域)时,应试图非正式地“证明”该数据项在当前位置具有确定的值。
-
对于所有的数组引用,是否每一个下标的值都在相应维规定的界限内?
-
对于所有的数组引用,是否每一个下标的值都是整数? 虽然在某些语言中这不是错误,但这样做是危险的。
-
对于所有的通过指针或引用变量的引用,当前引用的内存单元是否分配? 这就是所谓的“虚调用”(dangling reference)错误。当指针的生命周期大于所引用内存单元的生命周期时,错误就会发生。当指针引用了过程中的一个局部变量,而指针的值又被赋给了一个输出参数或一个全局变量,过程返回(释放了所引用的内存单元)结束,然后程序试图使用指针时,这种错误就会发生。与前面检查错误的方法类似,应试图非正式地“证明”,对于每个使用指针值的引用,引用的内存单元都存在。
-
如果一个内存区域具有不同属性的别名,当通过别名进行引用时,内存区域中的数据值是否具有正确的属性?
-
变量值的类型或属性是否与编译器所预期的一致? 当C、C++程序将某个记录读到内存中,并使用一个结构来引用它时,由于记录的物理表示与结构表示存在差异,这种情况就可能发生。
-
在使用的计算机上,当内存分配的单元小于内存可寻址的单元大小时,是否存在直接或间接地寻址错误? 例如,某些条件下,定长的位串不必以字节边界为起点,但是地址又总是只想字节边界的。如果程序计算一个位串的地址,稍后又通过该地址引用这个位串,可能会指向错误的内存位置。将一个位串参数传递给一个子程序时,也可能发生这种情况。
位串:由若干位组合起来形成位串,位串就是1和0的字串。
-
当使用指针或引用变量时,被引用的内存属性是否与编译器所预期的一致? 这种错误的一个例子是,当一个指向某数据结构的C++指针,被赋值给另外的数据结构的地址 。
-
假如一个数据结构在多个过程或子程序中被引用,那么每个过程或子程序对给结构的定义是否相同?
-
如果字符串由索引,当对数组进行索引或下标引用,字符串的边界取值是否有一个“仅差一个”(off-by-one)的错误。
off-by-one:大小差一错误是一类常见的程序设计错误,例如长度为5的数组中不应该出现a[5]
-
对于面向对象的语言,是否所有的继承需求都在实现类中得到了满足?
数据声明错误
- 是否所有的变量都进行了明确的声明? 虽然没有明确声明不一定是错误,但通常却是麻烦的源头。举例来说,如果一个程序的子程序接受一个数组参数,却为将参数定义为数组,对该数组的引用会被解释为一个函数调用,导致计算机试图将此数组当作程序执行。另外,如果某个变量在一个内部过程或程序块中没有明确声明,是否可以理解为该变量在这个程序块中被共有?
- 如果变量所有的属性在声明中没有明确说明,那么默认的属性能否被正确理解? 举例来说,在Java语言中,程序接收到的没有正确声明的默认属性往往是导致意外发生的源头。
- 如果变量在声明语句中被初始化,那么它的初始化是否正确? 在很多语言中,数组和字符串的初始化比较复杂,因此也称为容易出错的地方。
- 是否每个变量都被赋予了正确的长度和数据类型?
- 变量的初始化是否与其存储空间的类型一致?
- 是否存在相似名称的变量? 这种情况不一定是错误,但应该被视为警告,这些名称可能会在程序中发生混淆。
运算错误
- 是否存在不一致的数据类型(如非算术类型)的变量间的运算?
- 是否具有混合模式的运算? 例如,将浮点变量与一个整型变量做加法运算。这种情况并不一定是错误,但应该谨慎使用,确保程序语言的转换规则能被被正确理解。
- 是否有相同数据类型、不同字长变量间的运算?
- 赋值语句的目标变量间的数据类型是否小于右边表达式的数据类型和结果?
- 在表达式的运算中是否存在表达式间向上或向下溢出的情况? 也就是说,最终的结果看起来是个有效值,但中间结果对于编程语言的数据类型可能过大或过小。
- 除法运算中的除数是否可能为0?
- 如果计算机表达变量的基本方式是基于二进制的,你们运算结果是否不精确? 也就是说,在一个二进制计算机上,10*0.1很少会等于1.0 。
- 在特定场合,变量的值是否超出了有意义的范围?
- 对于包含一个以上操作符的表达式,赋值顺序和操作符的优先顺序是否正确?
- 整数的运算是是否有使用不当的情况,尤其是除法? 举例来说,如果i是一个整型变量,表达式2 * i / 2 == i 是否成立,取决于i是奇数还是偶数,或是先运算乘法还是先运算除法。
比较错误
- 是否有不同数据类型的变量之间的比较运算? 例如,将字符串与地址、日期或数字相比较?
- 是否有混合模式的比较运算,或不同长度的变量间的比较运算? 如果有,应确保程序能正常理解转换规则。
- 比较运算符是否正确? 程序员经常弄混淆“至多”、“至少”、“大于”、“不小于”、“小于”和“等于”等比较关系。
- 每个布尔表达式所叙述的内容是否都正确? 在编写涉及“与”、“或”、“非”的表达式时,程序员经常犯错。
- 布尔运算符的操作数是否是布尔类型的? 比较运算符和布尔运算符是否错误地混淆在一起
- 在二机制的计算机上,是否有用二进制表示的小数或浮点数的比较运算? 由于四舍五入,以及用二进制表示十进制的近似度,这往往是错误的根源。
- 对于那那些包含一个以上布尔运算符的表达式,赋值顺序以及运算符的优先顺序是否正确?
- 编译器计算布尔表达式的方式是否会对程序产生影响? 例如,语句if(x==0&&(x/y)>z) 对于有的编译器来说是可接受的,因为其认为一旦“与”运算符的一侧为false时,另一侧就不用计算,但是对于其他编译器来说,却可能引起一个被0除的错误。
控制流程错误
-
如果程序包含多条分支路径,比如有计算GO TO 语句,索隐变量的值是否会大于可能的分支数量? 例如,在语句中,i的取值是否总数1、2或3?
GO TO(200,300,400),i
-
是否所有的循环最终都终止了? 应涉及一个非正式的证据或论据来证明每一个循环都会终止。
-
程序、模块或子程序是否最终都终止了?
-
由于时机情况没有满足循环的入口条件,循环体是否有可能从未执行?
-
如果循环同时由迭代变量和一个布尔条件所控制(如一个搜索循环),如果循环越界了,后果会如何?
-
是否存在“仅差一个”的错误,如迭代数量恰恰多一次或少一次?
-
**如果编程语言中有语句组或代码块的概念(例如do-while()),是否每一组语句都有一个明确的while语句,并且do语句也与其相应的语句组对应?**或者,是否每一个左括号对应一个右括号? 目前的大多数编译器都能是别这些不匹配的情况。
-
是否存在不能穷尽的判断?
接口错误
- 被调用模块接收到的形参(parameter)数量是否等于调用模块发送的实参(argument)数量? 另外,顺序是否正确?
- 实参的属性(如数据类型和大小)是否与相应形参的属性相匹配?
- 实参的量纲是否与对应形参的量纲相匹配? 举例来说,是否形参以度为单位而实参以弧度为单位。
- 此模块传递给彼模块的实参数量,是否等于彼模块期望的形参数量?
- 此模块传递给彼模块的实参的属性,是否与并模块相应形参的属性相匹配?
- 此模块传递给彼模块的实参的量纲,是否与并模块相应形参的量纲相匹配?
- 如果调用了内置函数,实参的数量、属性、顺序是否正确?
- 如果某个模块或类有多个入口点,是否引入了与当前入口点无关的形参?
- 是否有子程序改变了某个原本仅为输入值的形参?
- 如果存在全局变量,在所有引用它们的模块中,它们的定义和属性是否相同?
- 常数是否以实参形式传递过?
输入/输出错误
- 如果对文件明确声明过,其属性是否正确?
- 打开文件的语句中各项属性的设置是否正确?
- 格式规范是否与I/O语句中的信息相吻合?
- 是否有足够的可用内存空间,来保存程序将读取的文件?
- 是否所有的文件在使用之前都打开了?
- 是否所有的文件在使用之后都关闭了?
- 是否判断文件结束的条件,并正确处理?
- 对I/O出错情况处理是否正确?
- 任何打印或显示的文本信息中是否存在拼写或语法错误?
- 程序是否正确处理了类似于“File Not Found”这样的错误?
其他检查
- 如果编译器建立了一个标识符交叉引用列表,那么对该列表进行检查,查看是否有变量从未被引用过,或仅被引用过一次。
- 如果编译器建立了一个属性列表,那么对每个变量的属性进行检查,确保没有赋予过不希望的默认属性值。
- 如果程序编译通过了,但计算机提供了一个或多个“警告”或“提示”信息,应对此逐一进行认真检查。“警告”信息指出编译器对程序某些操作的正确性有所怀疑;所有这些疑问都应进行检查。“提示”信息可能会罗列出没有声明的变量,或者时不利于代码优化的用法。
- 程序或模块是否具有足够的鲁棒性?也就是说,它是否对其输入的合法性进行了检查?
- 程序是否遗漏了某个功能?