张合花,张全法,齐永奇
(1.郑州大学 物理工程学院,郑州 450001;2.华北水利水电大学 机械学院,郑州 450045)
准确使用C/C++中等于运算符的研究
张合花1,张全法1,齐永奇2
(1.郑州大学 物理工程学院,郑州 450001;2.华北水利水电大学 机械学院,郑州 450045)
本文讨论C/C++程序设计中等于运算符的两种常见使用错误,即与赋值运算符混淆和用来判别两个浮点数是否相等。从程序员的角度给出了减少与赋值运算符混淆,以及不同情况下正确判别两个浮点数是否相等的方法。并利用VC 6.0设计了一个Add-in程序,能够自动发现有可能出现这两种错误的语句,提醒程序员注意,以便从集成开发环境的角度减少错误。
C/C++程序设计;等于运算符;Add-in
《C语言程序设计》是大多数高校开设的第一门计算机语言课程,《C++语言程序设计》则是许多高校开设的计算机语言进阶课程,同时C/C++也是广大工程技术人员最常用的程序设计语言之一。然而C/C++程序设计的灵活性也带来了一些负面影响,即容易出现各种各样的错误,其中就有等于运算符使用不慎造成的。
等于运算符也称为相等运算符,用来判别两个操作数是否相等。它由“==”构成,很容易与由“=”构成的赋值运算符混淆(以下简称为混淆错误),这在很多文献中有论述[1-4]。另外它还常被用来判别两个浮点数是否相等(以下简称为浮点数错误),导致运行结果不一定正确,例如文献[5]和文献[6]中的例程就出现了这种错误。本文主要研究在编程中如何尽量避免出现这两种错误。
假设有这样一个数学问题:在平面直角坐标系Oxy中有4个点A、B、C、D,各点的坐标值如图1所示,问C、D两点是否位于A、B两点所决定的直线上?在数学上很容易求得通过A、B两点的直线方程,进而知道C点在直线上而D点不在直线上。
为了利用计算机求解,用VC 6.0新建控制台类型的空白项目,项目名为Test。向其中添加名字为Test.cpp的源文件,文件内容如下:
图1 平面直角坐标系中的点
#include
void main(){
float xA=0.0f,yA=0.04f;
float xB=0.01f,yB=0.03f;
float xC=0.005f,yC=0.035f;
float xD=0.02f,yD=0.04f;
float y=(xC-xB)*(yA-yB)/(xA-xB)+yB;
if(y==yC)
cout<<"C is on line."< else cout<<"C is not on line."< y=(xD-xB)*(yA-yB)/(xA-xB)+yB; if(y=yD) cout<<"D is on line."< else cout<<"D is not on line."< 以上程序在编译、链接时均没有任何错误或警告。运行程序,输出为: C is on line. D is on line. 从运行结果看,对C点的判别是正确的,对D点的判别是错误的。针对错误的运行结果对程序进行分析可以发现,在第2个if语句的判断表达式中误将等于运算符写成了赋值运算符。这使得只要其右操作数不为0,均执行if子句而不执行else子句。 改正之后再运行程序,输出为: C is on line. D is not on line. 此时对C、D两点的判别均是正确的,好像程序没有问题了。其实不然,只要将程序中各数据的小数点向后移一位,输出将变为: C is not on line. D is not on line. 对C点的判别不再正确。 为什么运行结果会变得不再正确呢?原因在于浮点数在计算机中通常只能近似表示,即使两个在数学上应该相等的浮点数,在计算机中经过计算也很可能不相等。因此,用等于运算符判别两个浮点数是否相等很可能得不到正确结果。这种错误比混淆错误更加隐晦,很难在调试过程中被发现,所以减少这种错误的迫切性更强烈。 2.1 减少混淆错误的方法 作为程序员,首先要认识到判别两个操作数是否相等应该用两个等号而不是一个等号。其次,在编辑程序时应该时刻保持警惕,不要因为连续击键不到位等原因而漏掉两个等号中的一个。为了进一步减少这种错误,在条件允许的情况下应该尽量将非左值表达式(例如常量)放到等于运算符的左侧。因为此时一旦误用了赋值运算符,编译器会报告错误。 2.2 减少浮点数错误的方法 作为程序员,首先要认识到用等于运算符判别两个浮点数是否相等可能得到错误的结果。其次还要知道遇到此类问题该如何处理。由于浮点数在计算机中只能近似表示,无法判别两个浮点数是否严格相等,凡是判别两个浮点数是否相等的问题通常只能转化为判别它们是否近似相等。但是C/C++中没有近似相等运算符。为此,可以根据具体问题设置一个小量,当两个浮点数之差的绝对值小于该小量时认为二者近似相等[7]。如果需要对浮点数进行其他关系运算,也需要进行类似的处理,否则可能得不到正确结果。 对于以上经过修改的程序,再把“if(y==yC)”和 “if(y==yD)”分别修改为“if(fabs(y-yC)<1.0e-6)”和“if(fabs(y-yD)<1.0e-6)”。由于此时需要调用库函数fabs(),还应该在程序前面包含头文件math.h。再运行程序,输出为: C is on line. D is not on line. 结果正确。即使将数据恢复到移动小数点之前,结果依然正确。 对于某些问题,如果能够转化为整数运算,则仍然可以使用等于运算符。例如文献[5]和文献[6]的例程中,如果将对灰度值出现概率的求和修改为对灰度值出现次数的求和,再利用等于运算符判别求和结果是否等于灰度值出现的总次数,将可以避免处理某些图像时陷入死循环。 3.1 必要性和可行性 编程知识和经验不够丰富的程序员不具备主动防范上述错误的能力。即使编程知识和经验很丰富的程序员也难以通过主动防范杜绝上述错误。而仅根据调试结果又不太容易发现上述错误,因为有时候运行结果没有错误,或者因调试不完备导致存在错误的语句没有被执行。显然,如果能够设法让集成开发环境自动发现上述错误并发出警告,将非常有利于避免上述错误的发生。 目前,在高校和企业中广泛使用VC 6.0集成开发环境作为C/C++的教学和研发平台。它可以外挂Add-in对正在开发的程序进行分析,查找可能出现的问题。Add-in已经被成功地用来减少重复声明复合类型的风险[8]、使用goto语句的错误[9]以及使用后置自增自减运算符的非预期结果[10]。因此,设计一个能够自动查找等于运算符使用错误的Add-in是可行的。 3.2 查找错误的原理和困难 查找错误的原理很简单。对于在程序中查找到的每一个赋值运算符,如果能够推断该处应该使用等于运算符则说明该处出现了混淆错误,否则该处没有错误。对于在程序中查找到的每一个等于运算符,如果能够推断它作用在浮点数上,则说明该处出现了浮点数错误,否则该处没有错误。按照此原理可以避免对于上述两种错误的漏报。 困难在于如何准确地进行推断。由于在Add-in中无法知道程序所描述的实际问题,很难推断赋值运算符是否应该为等于运算符。推断等于运算符操作数的类型也很不容易,因为其操作数可以是各种各样的表达式,甚至可以没有类型,例如宏形参。即使其操作数为变量也不容易推断其类型,因为变量类型的声明可能不在本文件中,例如在包含文件中,包含文件还可以嵌套;内层变量的类型可以与外层同名变量的类型不同等等。 3.3 解决问题的办法 如上所述,在Add-in中很难推断是否一定产生了错误,然而程序员对程序所描述的问题是清楚的。因此,将可能出现错误的位置及可能的错误类型报告给程序员去判断,可以得到准确的结果并由其采取正确的措施。但是这又会带来新的问题。等于运算符使用得比较少,即使将所有使用它的地方报告给程序员,误报量也不大。但是如果将所有使用赋值运算符的地方报告给程序员,必将产生大量误报,因为赋值运算是一种很常见的运算。 可以根据等于运算符的属性减少误报。等于运算符属于关系运算符,最有可能出现的地方首先是if语句的判断条件表达式,其次是条件运算符的判断条件表达式,然后是for、while和do…while语句的循环条件表达式。而这些地方使用赋值运算符的可能性是比较低的。仅在这些地方寻找赋值运算符并报告给程序员可以减少误报。不过这样做有可能出现漏报,因为等于运算符允许出现在这些地方之外。 3.4 Add-in的开发 有关如何在VC 6.0中创建Add-in类型的项目、在Add-in的哪个函数中访问项目文件、访问项目文件之前应该做哪些准备工作、如何逐一访问项目中的所有源文件和头文件、如何在内存中对各个文件的内容进行简化以及如何输出警告信息等,可以参考文献[8]~文献[10]。下面仅简单介绍一下查找可能存在错误之语句的位置及类型的方法步骤。 (1)从文件开头向后搜索“==”,一直到文件末尾。每搜索到一处,记录正在分析的文件名、“==”所在的行号和问题类型。此时问题类型为“使用==”。 (2)从文件开头向后搜索“?”,一直到文件末尾。每搜索到一处,再从该处向前寻找最近的运算符。如果它为赋值运算符(注意等号未必是赋值运算符,下同,不再说明),记录正在分析的文件名、“?”所在的行号和问题类型。此时问题类型为“?:中使用=”。 (3)从文件开头向后搜索“if”(注意防止它是其他标识符的一部分,下同,不再说明),一直到文件末尾。每搜索到一处,再从该处向后寻找最近且配对的左、右圆括号,并在其间搜索赋值运算符。如果搜索到了,记录正在分析的文件名、“if”所在的行号和问题类型。此时问题类型为“if中使用=”。 (4)从文件开头向后搜索“for”,一直到文件末尾。每搜索到一处,再从该处向后寻找最近且配对的左、右圆括号,接着在其间寻找两个“;”,并在这两个“;”之间搜索赋值运算符。如果搜索到了,记录正在分析的文件名、“for”所在的行号和问题类型。此时问题类型为“for中使用=”。 (5)从文件开头向后搜索“while”,一直到文件末尾。每搜索到一处,再从该处向后寻找最近且配对的左、右圆括号,并在其间搜索赋值运算符。如果搜索到了,记录正在分析的文件名、“while”所在的行号和问题类型。此时问题类型取决于上述右圆括号后面的第一个非空白字符。如果它为“;”,则问题类型为“do…while中使用=”,否则为“while中使用=”。 3.5 Add-in的运行结果 Add-in开发好后,按照文献[10]中所述方法使其外挂到VC 6.0中。然后重新打开上述Test项目,回退到未进行修改之前,再执行Build菜单命令,将弹出如图2所示的对话框,给出警告信息。经过测试,证明了它在大多数情况下能够发现等于运算符使用中可能存在的问题。 图2 发现问题时弹出的对话框 讨论了C/C++中主动减少等于运算符使用错误的方法。开发的Add-in程序能够自动寻找在VC 6.0中使用等于运算符时可能出现错误的地方,有助于程序员及时发现问题并采取正确措施,可以为广大学生和工程技术人员学习、掌握及运用C/C++程序设计提供帮助。事实上,当开发的Add-in发现可能发生混淆错误的地方时,实际上有可能是其他类型的错误。例如由于击键不到位,应该使用小于等于、大于等于或不等于运算符的地方变成了使用赋值运算符等。因此也可以减少出现这些错误的可能性。这可以算是所开发的Add-in的额外收获。 [1]谭浩强.C程序设计[M].4版.北京:清华大学出版社, 2010:359-360. [2]赵宝琴,袁志民.C/C++上机实验中的典型错误[J].实验室科学,2005(3):92-93. [3]万权性,熊力维,李微.C语言常见错误原因分析及防范[J].福建电脑,2010(12):167-168. [4]刘伟,支联合.C语言教学中遇到的问题及解决办法[J].重庆科技学院学报:自然科学版,2009,11(2):136-138. [5]何斌,马天予,王运坚,等.Visual C++数字图像处理[M].2版.北京:人民邮电出版社,2002:583-594. [6]郎锐.数字图像处理学——Visual C++实现[M].北京:北京希望电子出版社,2002:387-395. [7]周冠方.C语言中浮点数精度问题分析[J].湖北工业职业技术学院学报,2015,28(3):97-99. [8]张全法,齐永奇.用Add-in提高VC 6.0集成开发环境的查错能力研究[J].中原工学院学报,2009,20(3):47-50. [9]张全法,陈倩.用Add-in减少VC 6.0中goto语句使用错误的研究[J].中原工学院学报,2013,24(2):57-60. [10]张合花,齐永奇.减少后置自增自减运算符非预期结果的研究[J].中原工学院学报,2015,26(1):87-90. (责任编辑 赵冰) Study on Reducing Use Errors of Equality Operator in C/C++ Programming ZHANG He-hua1,ZHANG Quan-fa1,QI Yong-qi2 (1.School of Physics and Engineering, Zhengzhou University, Zhengzhou 450001, China; 2.North China University of Water Resources and Electric Power, Zhengzhou 450045, China) Two kinds of common use errors of equality operator in C/C++ programming are discussed, one is to confuse with assignment operator and the other is to determine whether two floating point numbers are equal.For the programmers, some methods to reduce confusion with assignment operator and determine whether two floating point numbers are equal under different conditions are given.An Add-in is developed with VC 6.0 to find where maybe appear these kinds of errors and to notify the programmer to examine his program, thus to reduce the probability of errors from the perspective of the integrated development environment. C/C++ programming; equality operator; Add-in 2015-12-21 张合花(1970—),女,河南汤阴人,郑州大学物理工程学院技师,主要研究方向为计算机信息处理。 10.13783/j.cnki.cn41-1275/g4.2016.02.023 TP311.52 A 1008-3715(2016)02-0112-042 主动减少错误的方法
3 利用Add-in减少错误
4 结束语