减少后置自增自减运算符非预期结果的研究
2016-01-20张合花齐永奇
张合花, 齐永奇
(1.郑州大学 物理工程学院, 郑州 450001; 2.华北水利水电大学 机械学院, 郑州 450045)
减少后置自增自减运算符非预期结果的研究
张合花1, 齐永奇2
(1.郑州大学 物理工程学院, 郑州 450001; 2.华北水利水电大学 机械学院, 郑州 450045)
摘要:在VC 6.0中调用函数时,如果实际参数表达式中使用了后置自增、自减运算符,发布版的运行结果可能不同于调试版的预期。本文在分析问题构成条件的基础上,选择编写Add-in类型的程序来发现这种问题并提醒程序员修改源程序,并在讨论有关注意事项后列出了主要开发步骤。所开发的Add-in程序可自动分析VC 6.0活动项目中的所有源文件和头文件,检查是否存在这种问题,从而降低出现非预期结果的可能性。
关键词:自增运算符;自减运算符;调试版;发行版;Add-in;VC 6.0
由于高版本VC软件的局限性,VC 6.0仍然是功能最强大、使用范围最广泛的软件开发工具[1]。它的一个重要功能是可以在调试版中单步执行和跟踪,排除算法错误后再生成小而快的发布版可执行文件[2]。目前,许多高校用它作为C/C++的教学平台,程序员也用它进行各种各样的应用开发。C/C++的自增、自减运算符具有简便、高效的特点,常被用来提高程序的运行速度。
使用自增、自减运算符的主要问题在于当它被用于复杂表达式的时候会降低可移植性。这是因为标准C/C++没有规定子表达式的求值顺序,而允许各编译系统自己安排[3-5]。我们在实践中发现,在VC 6.0中调用函数时,如果实际参数表达式中使用了后置自增、自减运算符,并且受该运算符作用的对象在多个实际参数表达式中同时出现,那么发布版的运行结果可能与调试程序时的预期结果不相同。为此,本文用VC 6.0开发了一个能够对源程序进行自动分析的Add-in程序。该程序一旦发现这种情况,就会提醒程序员修改源程序。
1问题及构成条件
1.1问题
我们用VC 6.0新建了一个控制台类型的项目,项目名为PostfixError。其中有一个名字为Postfix.cpp的源文件,文件内容如下:
#include
void func(int i,int j){
cout<<“i =”<
void main(){ int a = 1; func(a,a--); }
在调试版下运行程序,输出为:
i=1,j=1
而在发布版下,输出为:
i=0,j=1
显然二者存在差别,但是在两个版本下进行编译、链接时均没有任何错误或警告。将自减运算符改为自增运算符后,问题同样存在。
程序员往往在调试版中调试算法,如果运行结果符合预期,则认为算法正确,发布程序时往往不再全面测试运行结果。而且对于复杂的源程序来说,即使是调试版,也很难进行全面的测试。这就可能产生错误的程序运行结果。
1.2问题的构成条件
实践发现,在VC 6.0中使用自增、自减运算符时,调试版与发布版的运行结果大多数情况下相同,仅当同时满足以下条件时才可能出现不同:①自增或自减运算符出现在函数调用的实际参数表达式中;②该运算符为后置的而非前置的;③所调用的函数具有至少2个形式参数,并且为全局函数而非成员函数;④该运算符的作用对象至少在所调用函数的2个实际参数表达式中同时出现。
2解决方案及有关事项
2.1用Add-in查找问题
文献[6]列举、比较了3 条可以用来与VC 6.0交互的途径,并指出编写Add-in类型的程序是与VC 6.0交互的最好选择。在Add-in程序中可以对VC 6.0活动项目中的所有源文件和头文件进行分析,查找各种各样的问题。它已经被成功地用来减少重复声明复合类型可能带来的风险[6]以及使用goto语句可能出现的死循环[7]。因此,我们可选择编写Add-in类型的程序来查找符合上述条件的情况,发现问题时输出警告信息。
2.2确定源程序分析时机
Add-in程序以动态链接库的形式存在,无法在Windows中直接运行,却可以在VC 6.0中加载并运行。VC 6.0根据已经发生或即将发生的事件调用Add-in程序中的不同函数,实现与Add-in程序的交互。这些事件包括打开或者关闭文档、即将执行Build菜单命令或执行完毕Build菜单命令等。因此,Add-in程序可选择不同的时机对源程序进行分析。但是文件分析非常复杂,为了避免诸如语法错误等造成的分析困难,一般选择在源程序无编译和链接错误时对其进行分析。
为了仅处理属于源程序的文件,并且不产生遗漏,还应该在分析之前由Add-in程序通知VC 6.0,让其保存程序员针对项目所做的改动,包括添加和删除文件等。这是因为默认情况下执行Build菜单命令时仅自动保存针对文件内容所做的改动。
2.3准确查找和定位
(1)发现问题时给出的警告信息应该完整和准确。因为同一个函数可能在多处使用不同形式的实际参数调用,所以当发现问题时仅给出函数调用表达式及其所在文件的名字是不够的,还必须指出问题出现在哪一行以及哪个或者哪些变量存在问题,才有助于程序员迅速解决问题。
(2)分析文件时需要排除那些不满足条件的情况,以便尽量避免误报。我们很容易判定变量情况是否满足问题构成条件①③④,但是很难判定是否满足条件②。这是因为条件②的判定依据是该运算符的左侧存在作用对象,并且该作用对象为左值表达式,而左值表达式的提取与判定非常困难。因此,目前仅限于处理变量(包括数组元素,下同),即只要该运算符左侧为变量,就认为它是后置的。
(3)分析文件时需要避免字符串或注释中存在符合条件的内容而产生误报,同时避免将其他内容当作字符串或注释而产生漏报。这可以通过简化字符串和注释来实现。但是,简化只能在内存中进行,不能改变原内容。为了方便简化字符串和注释,通常还需简化字符常量。在所有这些处理中,均不能改变行序号,以免在给出行序号时出错。
3实现解决方案的主要步骤
由于Add-in程序的代码较长,这里仅给出主要开发步骤及实现的功能。
3.1创建Add-in项目
在VC 6.0中执行File/New菜单命令,弹出对话框。在Projects选项卡的列表框中选择DevStudio Add-in Wizard类型,在项目名编辑框中输入项目名FindPostfixError。点击OK按钮,再弹出对话框。取消复选框Provides a toolbar的选中状态,使其只能自动运行,以免有错误时被人工运行。确保复选框Responds to Developer Studio events处于选中状态,使其响应VC 6.0的事件,以便在正确的时机自动运行。点击Finish按钮,再点击OK按钮,完成Add-in项目的创建。
3.2保存项目改动
在工作区的ClassView面板上展开树状类视图,可以找到类中类XApplicationEvents的成员函数BuildFinish()。在该函数中,当其形式参数nNumErrors为0时对源程序中的文件进行分析,才符合前述分析时机的要求。这是因为VC 6.0执行Build菜单命令之后才自动调用该函数,而nNumErrors的值为0说明没有编译、链接错误。
在分析文件之前通过数据成员m_pCommands调用成员函数GetApplicationObject(),获得指向VC 6.0的Application对象指针。通过该指针调用成员函数ExecuteCommand()来执行WorkspaceSave命令,让VC 6.0保存程序员针对项目的改动。
3.3简化文件内容
通过上述Application对象指针调用成员函数get_ActiveProject(),获得指向活动项目的指针。通过活动项目指针调用成员函数get_FullName(),获得扩展名为dsp的项目信息文件的全名。根据此文件的内容获得活动项目中所有文件的全名。根据文件的扩展名筛选出源文件和头文件,将其内容读入内存,并在内存中进行简化。
(1)将最前面的字符设为当前字符。
(2)如果该字符为单引号则转到(3),如果该字符为双引号则转到(4),如果该字符及其后字符均为斜杠则转到(5),如果该字符为斜杠且其后字符为星号则转到(6)。除此之外,将下一字符设为当前字符继续进行判断,直到处理完所有字符。
(3)说明正在处理的字符常量,向后搜索与其配对的单引号,并将它们之间的内容删除。之后将配对的单引号后面的字符设为当前字符并返回(2)。
(4)说明正在处理的字符串常量,向后搜索与其配对的双引号,并将它们之间的内容删除。之后将配对的双引号后面的字符设为当前字符并返回(2)。
(5)说明正在处理的以双斜杠作为起始标记的注释,删除双斜杠及其后被注释掉的内容。之后将注释内容结束位置所在行的换行符设为当前字符并返回(2)。
(6)说明正在处理的以/*作为起始标记和以*/作为结束标记的注释块,删除/*和*/以及它们之间的内容。之后将注释结束标记之后的字符设为当前字符并返回(2)。
需要说明的是,以上处理中皆不能删除表示文件内容换行的换行符,而字符常量、字符串常量和两种形式的注释都可能跨行。
3.4查找问题
对经过上述简化处理的内存进行处理,查找可能存在问题的自增、自减运算符。
(1)将最前面的字符设为起始位置。
(2)从起始位置向后搜索自增、自减运算符。如果找到了,就记录其位置并转到(3),否则结束。
(3)从该运算符的左侧字符向前搜索未配对的左圆括号,遇到分号或花括号则停止。如果找到了,就记录其位置并转到(4),否则说明它所处的表达式不是函数调用表达式,需转到(8)。
(4)提取左圆括号至该运算符左侧字符之间的内容,并在提取内容中从后向前提取变量名。如果提取成功,说明是后置的,则记录变量名并转到(5),否则需转到(8)。
(5)从该运算符的右侧字符向后搜索未配对的右圆括号,遇到分号或花括号则停止。如果找到了,就记录其位置并转到(6),否则说明它所处的表达式不是函数调用表达式,则转到(8)。
(6)提取上述左圆括号至右圆括号之间的内容,然后在提取的内容中搜索(4)中记录的变量名并记录每次出现的位置。搜索时需要注意它不能是另一个标识符的一部分。如果变量名出现次数小于2,则转到(8),否则在任意两相邻位置之间搜索逗号。如果找到了,则转到(7),否则说明该变量没有在多个实际参数表达式中同时出现,转到(8)。
(7)在左圆括号之前提取函数名。如果提取失败,说明这一对圆括号不是函数调用运算符,需转到(8),否则检查函数名前面是否为点运算符或箭头运算符。如果是,则说明调用的函数为成员函数,也转到(8),否则提取完整的函数调用表达式并将其记录到链表,同时记录该函数名所在行的序号、(4)中记录的变量名及所处理文件的名字。
(8)将自增或自减运算符的右侧字符设为起始位置,然后返回(2)。
需要说明的是,以上所有搜索都必须在有效范围内进行,搜索自增或自减运算符、提取变量名或函数名时皆应考虑可能存在续行问题。
3.5输出警告信息
如果经过上述处理得到的链表不为空,则需要先检查有没有相同项。如果有就合并,以免重复报告。然后,以对话框的形式发出警告,提醒程序员注意。最后,还要清除链表,以便在下次执行Build菜单命令时不受以前的影响。
4Add-in的运行
对于Add-in 类型的FindPostfixError项目,执行Build菜单命令,生成动态链接库文件。然后,执行Tools/Customize菜单命令,在Add-ins and Macro Files选项卡上点击Browse按钮找到该文件,VC 6.0将自动地注册并启用它,让其响应VC 6.0的事件。因此,一旦完成Add-in程序的开发,就可以将其应用到任何安装了VC 6.0的计算机上,帮助程序员降低程序出错的可能性。
打开控制台类型的PostfixError项目,执行Build菜单命令,将弹出图1所示的对话框。这说明所编写的Add-in程序发现了PostfixError项目中存在着发布版运行结果不同于调试版的可能性,并给出了详细信息,包括文件名、函数调用表达式、问题变量及其所在行的序号等。
图1 发现问题时弹出的对话框
运行结果表明,Add-in程序在多数情况下,能够发现使用后置自增、自减运算符时可能存在问题的函数调用表达式。
5结语
所开发的Add-in程序能够自动地寻找在VC 6.0中使用自增、自减运算符时有可能出现非预期结果的地方,有助于及时发现问题,避免造成不良后果。因此,它可以为广大学生和工程技术人员学习、掌握和运用VC 6.0,进行程序设计提供帮助。在实践中,需要设法避免自增、自减运算符左侧的作用对象为变量及数组元素之外的左值表达式时出现的漏报现象。
参考文献:
[1]王育坚.Visual C++面向对象编程教程[M].北京:清华大学出版社,2003.
[2]王华.VC++ 6.0 Release和Debug的不同[J].科技信息,2010(11):475.
[3]谭浩强.C程序设计(第四版)[M].北京:清华大学出版社,2010:366-367.
[4]邢莉莉.解析C语言中自增运算符问题[J].科技风,2009(20):8.
[5]黄玉兰.有关C语言输出函数中的自增自减运算符在不同编译环境中的探讨[J].科技致富向导,2013(20):26-27.
[6]张全法,齐永奇.用Add-in提高VC 6.0集成开发环境的查错能力研究[J].中原工学院学报,2009,20(3):47-50.
[7]张全法,陈倩.用Add-in减少VC 6.0中goto语句使用错误的研究[J].中原工学院学报,2013,24(2):57-60.
(责任编辑:王长通)
Study on Reducing Unexpected Result of Postfix
Increment or Decrement Operator
ZHANG He-hua1, QI Yong-qi2
(1. Zhengzhou University, Zhengzhou 450001;
2. North China University of Water Resources and Electric Power, Zhengzhou 450045, China)
Abstract:When developing programs with VC 6.0, if postfix increment or decrement operator is used in actual argument expression of function, the running result of release configuration may be unexpectedly different from that of debug configuration. Based on analyzing the conditions under which this kind of problem will appear, chosen Add-in to find this kind of problem for notifying the programmer to modify its program, discussed some special questions in practice, and given the main steps in developing the Add-in. The Add-in can be used to analyze all source files and header files of the active project in VC 6.0 to determine if there is this kind of problems, thus the probability of producing unexpected result is reduced.
Key words:increment operator; decrement operator; debug configuration; release configuration; Add-in; VC 6.0
文章编号:1671-6906(2015)01-0091-04
作者简介:朱登雷(1986-),男,河南安阳人,硕士生。
基金项目:国家自然科学基金项目(51077134)
收稿日期:2014-03-21
中图分类号:TP311.52
文献标志码:ADOI:10.3969/j.issn.1671-6906.2015.01.022