代码检查常见错误列表--《软件测试的艺术》

本文详细列举了代码检查中常见的错误,包括数据引用错误、声明错误、运算错误、比较错误、控制流错误、接口错误、输入/输出错误和其他检查。对于每个错误类型,提供了具体的检查点和示例,旨在帮助程序员提高代码质量,避免运行时问题。
摘要由CSDN通过智能技术生成

1.数据引用错误

  1. 是否有引用的变量未赋值或未初始化?这可能是最常见的编程错误,在各种环境中都可能发生。在引用每个数据项(如变量、数组元素、结构中的域)时,应试图非正式地“证明”该数据项在当前位置具有确定的值。
  2. 对于所有的数组引用,是否每一个下标的值都在相应维规定的界限之内?
  3. 对于所有的数组引用,是否每一个下标的值都是整数?虽然在某些语言中这
    不是错误,但这样做是危险的。
  4. 对于所有的通过指针或引用变量的引用,当前引用的内存单元是否分配?这就是所谓的 “虚调用(dangling reference)” 错误。当指针的生命期大于所引用内存单元的生命期时,错误就会发生。当指针引用了过程中的一个局部变量,而指针的值又被赋给一个输出参数或一个全局变量,过程返回(释放了引用的内存单元)结束,而后程序试图使用指针的值时,这种错误就会发生。与前面检查错误的方法类似,应试图非正式地“证明”,对于每个使用指针值的引用,引用的内存单元者都存在。
  5. 如果一个内存区域具有不同属性的别名,当通过别名进行引用时,内存区域中的数据值是否具有正确的属性?在 FORTRAN 语言中对 EQUIVALENCE 语句使用,或 COBOL 语言中对 REDEFINES 语句使用的地方,都可能发生这种错误。例如,一个 FORTRAN 语言程序包含一个实型变量 A 和一个整型变量 B,两者都通过使用 EQUIVALENCE 语句而成为同一内存区域的别名。如果程序先对 A 赋值,然后又引用变量 B,由于机器可能会将内存中用浮点位表示的实数当作整数,在这种情况下错误就可能发生。
  6. 变量值的类型或属性是否与编译器所预期的一致?当 C、C++或 COBOL 程序将某个记录读到内存中,并使用一个结构来引用它时,由于记录的物理表示与结构定义存在差异,这种情况下错误就可能发生。
  7. 在使用的计算机上,当内存分配的单元小于内存可寻址的单元大小时,是否存在直接或间接的寻址错误?例如,在某些条件下,定长的位串不必以字节边界为起点,但是地址又总是指向字节边界的。如果程序计算一个位串的地址,稍后又通过该地址引用这个位串,可能会指向错误的内存位置。将一个位串参数传送给一个子程序时,也可能发生这种情况。
  8. 当使用指针或引用变量时,被引用的内存的属性是否与编译器所预期的一致?这种错误的一个例子是,当一个指向某个数据结构的 C++指针,被赋值为另外的数据结构的地址。
  9. 假如一个数据结构在多个过程或子程序中被引用,那么每个过程或子程序对该结构的定义是否都相同。
  10. 如果字符串有索引,当对数组进行索引操作或下标引用,字符串的边界取值是否有“仅差一个(off-by-one)”的错误?
  11. 对于面向对象的语言,是否所有的继承需求都在实现类中得到了满足?

2.数据声明错误

  1. 是否所有的变量都进行了明确的声明?没有明确声明虽然不一定是错误,但通常却是麻烦的源头。举例来说,如果一个程序的子程序接收一个数组参数,却未将该参数定义为数组(如用 DIMENSION 语句),对该数组的引用(如 C=A(I))会被解释为一个函数调用,导致计算机试图将此数组当作程序执行。另外,如果某个变量在一个内部过程或程序块中没有明确声明,是否可以理解为该变量在这个程序块中被共用?
  2. 如果变量所有的属性在声明中没有明确说明,那么默认的属性能否被正确理解?举例来说,在 Java 语言中,程序接收到的默认属性往往是导致意外情况发生的源头。
  3. 如果变量在声明语句中被初始化,那么它的初始化是否正确? 在很多语言中,数组和字符串的初始化比较复杂,因此也成为容易出错的地方。
  4. 是否每个变量都被赋予了正确的长度和数据类型?
  5. 变量的初始化是否与其存储空间的类型一致?举例来说,如果 FORTRAN 语言子程序中的一个变量在每次调用子程序时都需要重新初始化一次,那么必须使用赋值语句对其初始化,而不应该用 DATA 语句。
  6. 是否存在着相似名称的变量(如 VOLT 和 VOLTS)?这种情况不一定是错误,但应被视为警告,这些名称可能会在程序中发生混淆。

3.运算错误

  1. 是否存在不一致的数据类型(如非算术类型)的变量间的运算?
  2. 是否有混合模式的运算?例如,将浮点变量与一个整型变量做加法运算。这种情况并不一定是错误,但应该谨慎使用,确保程序语言的转换规则能够被正确理解。看看下面的 Java 程序片段.显示了整数运算中可能发生的取整误差:
int x = 1;
int y = 2;
int z = 0;
z = x/y; 
System.out.println("z = " + z);
OUTPUT:
z = 0 
  1. 是否有相同数据类型不同字长变量间的运算?
  2. 赋值语句的目标变量的数据类型是否小于右边表达式的数据类型或结果?
  3. 在表达式的运算中是否存在表达式向上或向下溢出的情况,也就是说,最终的结果看起来是个有效值,但中间结果对于编程语言的数据类型可能过大或过小。
  4. 除法运算中的除数是否可能为 0?
  5. 如果计算机表达变量的基本方式是基于二进制的,那么运算结果是否不精确?也就是说,在一个二进制计算机上,10×0.l 很少会等于 l.0。
  6. 在特定场合,变量的值是否超出了有意义的范围?例如,对变量PROBABILITY 赋值的语句可能需要进行检查,保证赋值始终为正且不大丁 1.0。
  7. 对于包含一个以上操作符的表达式,赋值顺序和操作符的优先顺序是否正确?
  8. 整数的运算是否有使用不当的情况,尤其是除法?举例来说,如果 i 是一个整型变量,表达式 2*i/2 == i 是否成立,取决于 i 是奇数还是偶数,或是先运算乘法,还是先运算除法。

4.比较错误

  1. 是否有不同数据类型的变量之间的比较运算,例如,将字符串与地址、日期或数字相比较?
  2. 是否有混合模式的比较运算,或不同长度的变量间的比较运算?如果有,应确保程序能正确理解转换规则。
  3. 比较运算符是否正确?程序员经常混淆“至多”、“至少”、“大于”、“不小于”、“小于”和“等于”等比较关系。
  4. 每个布尔表达式所叙述的内容是否都正确?在编写涉及“与”、“或”或“非”的表达式时,程序员经常犯错。
  5. 布尔运算符的操作数是否是布尔类型的?比较运算符和布尔运算符是否错误地混住了一起?这是一类经常会犯的错误。这里我们描述几个典型错误的例子。如果想判断 i 是否在 2~10 之间,表达式 2<i<10 是不正确的;相反,正确的应该是(2<i)&&(i<10)。如果想判断 i 是否大于 x 或 y,表达 i>x ||y 也是不正确的,正确的应该是(i>x)||(i>y)。如果要比较三个数字是否相等,表达式if(a==b==c)的实际意思却大相庭。如果需要验证数学关系 x>y>z,正确的表达式应该是(x>y)&&(y>z)
  6. 在二进制的计算机上,是否有用二进制表示的小数或浮点数的比较运算?由于四舍五入,以及用二进制表示十进制数的近似度,这往往是错误的根源。
  7. 对于那些包含一个以上布尔运算符的表达式,赋值顺序以及运算符的优先顺序是否正确?也就是说,如果碰到如同(if((a==2)&&(b==2)||(c==3))的表达式,程序能否正确理解是“与”运算在先,还是“或”运算在先?
  8. 编译器计算布尔表达式的方式是否会对程序产生影响?例如,语句if((x==0 && (x/y)>z)对于有的编译器来说是可接受的,因为其认为一旦“与”运算符的一侧为 FALSE 时,另一侧就不用计算;但是对于其他编译器来说,却可能引起一个被 0 除的错误。

5.控制流错误

  1. 如果程序包含多条分支路径,比如有计算 GOTO 语句,索引变量的值是否会大于可能的分支数量?例如,在语句GOTO (200,300,400),i中,i 的取值是否总是 1、2 或 3?
  2. 是否所有的循环最终都终止了?应设计一个非正式的证据或论据来证明每一个循环都会终止。
  3. 程序、模块或子程序是否最终都终止了?
  4. 由于实际情况没有满足循环的入口条件,循环体是否有可能从未执行过?如果确实发生这种情况,这里是否是一处疏漏?例如,如果循环以下面的语句作为开头:
for {i==x;i<=z;i++} {
...
}
while(NOTFOUND){
...
}

当 NOTFOUND 初始时就为假,或者 x 大于 z 时,情况会如何呢’
5. 如果循环同时由迭代变量和一个布尔条件所控制(如一个搜索循环),如果循环越界(fall-through)了,后果会如何?例如,伪指令循环以D0 I=1 to TABLESIZE WHILE (NOTFOUND)开头,如果 NOTFOUND 永不为假,会发生什么结果呢?
6. 是否存在“仅差一个”的错误,如迭代数量恰恰多一次或少一次?这在从 0开始的循环中是常见的错误。我们会经常忘记将“0”作为一次计数。举例来说,如果想编写一段 Java 代码执行 10 次循环,下面的语句是错误的,因为它执行了 11次:

for(int i=0;i<=10;i++){
 System.out.println(i);
}

正确的应该是执行 10 次循环

for(int i=0;i<=9;i++) {
 System.out.println(i);
}
  1. 如果编程语言中有语句组或代码块的概念(例如 do-while 或{…}),是否每一组语句都有一个明确的 while 语句,并且 do 语句也与其相应的语句组对应?或者,是否每一个左括号都对应有一个右括号?目前的大多数编译器都能识别出这些不匹配的情况。
  2. 是否存在不能穷尽的判断?举例来说,如果一个输入参数的预期值是 1,2或 3,当参数值不为 l 或 2 时,在逻辑上是否假设了参数必定为 3?如果是这样的话,这种假设是否有效?

6.接口错误

  1. 被调用模块接收到的形参(parameter)数量是否等于调用模块发送的实参(argument)数量?另外,顺序是否正确?
  2. 实参的属性(如数据类型和大小)是否与相应形参的属性相匹配?
  3. 实参的量纲是否与对应形参的量纲相匹配?举例来说,是否形参以度为单位而实参以弧度为单位?
  4. 此模块传递给彼模块的实参数量,是否等于彼模块期望的形参数量?
  5. 此模块传递给彼模块的实参的属性,是否与彼模块相应形参的属性相匹配?
  6. 此模块传递给彼模块的实参的量纲,是否与彼模块相应形参的量纲相匹配?
  7. 如果调用了内置函数,实参的数量,属性,顺序是否正确?
  8. 如果某个模块或类有多个入口点,是否引用了与当前入口点无关的形参?下面 PL/1 程序的第二个赋值语句就存在这种错误:
A: PROCEDURE(W,X);
W=X+1;
RETURN
B: ENTRY(Y,Z);
Y=X+Z;
END;
  1. 是否有子程序改变了某个原本仅为输入值的形参?
  2. 如果存在全局变量,在所有引用它们的模块中,它们的定义和属性是否相同?
  3. 常数是否以实参形式传递过?在一些用 FORTRAN 语言编写的程序中,诸如CALL SUBX(J, 3)的语句是很危险的,因为如果子程序SUBX 对其第二个形参进行赋值,常数 3的值将会被改变。

7.输入/输出错误

  1. 如果对文件明确声明过,其属性是否正确?
  2. 打开文件的语句中各项属性的设置是否正确?
  3. 格式规范是否与 I/O 语句中的信息相吻合?举例来说,在 FORTRAN 语言中,是否每个 FORMAT 语句都与相应的 READ 或 WRITE 语句相一致(就各项的数量和属性而言)?
  4. 是否有足够的可用内存空间,来保留程序将读取的文件?
  5. 是否所有的文件在使用之前都打开?
  6. 是否所有的文件在使用之后都关闭了?
  7. 是否判断文件结束的条件,并正确处理?
  8. 对 I/O 出错情况处理是否正确?
  9. 任何打印或显示的文本信息中是否存在拼写或语法错误?

8.其他检查

  1. 如果编译器建立了一个标识符交叉引用列表,那么对该列表进行检查,查看是否有变量从未引用过,或仅被引用过一次。
  2. 如果编译器建立了一个属性列表,那么对每个变量的属性进行检查,确保没有赋予过不希望的默认属性值。
  3. 如果程序编译通过了,但计算机提供了一个或多个“警告”或“提示”信息,应对此逐一进行认真检查。“警告”信息指出编译器对程序某些操作的正确性有所怀疑,所有这些疑问都应进行检查。“提示”信息可能会罗列出没有声明的变量,或者是不利于代码优化的用法。
  4. 程序或模块是否具有足够的鲁棒性?也就是说,它是否对其输入的合法性进行了检查?
  5. 程序是否遗漏了某个功能?

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值