维普资讯 http://www.cqvip.com 第1期(总第62期) 2008年1月 山西广播电视大学学报 No.1 中。自增 自蕊,运算二义性分析 口张志强 (太原师范学院计算机系。山西太原030012) 摘要:C++中自增、自减运算符应用广泛,但使用起来具有一定的复杂性与风险性,易 l发二 义性fq题。通过实例进行归类分析,揭示自增、自减运算符副作用对二义性的影响,给出解决方法, 以保证对其使用的安全性。 关键词:二义性;自增;自减 中图分类号:TP312文献标识码:B文章编号:1008----.8350(2008)0l_-0043一】2 在C++中允许一个表达式中可使用多个自增(自减) 运算符,这使得C++语言使用更灵活、简洁,生成的代 码质量更高,因此被广泛应用。但也正是这一点,使得表 示方法不规范,导致不易理解,进而产生二义性。如何正 确有效的对其使用,成为C++教学研究中重要的课题。 一二义性是指编译器不能分析通过的二义性。隐性二义性指 编译器分析通过,但不同编译器可能产生不同解,结果常 与程序员愿望不符的情况。理解二义性指,编译通过,且 所有编译器处理结果一致,但与程序员预期不符的案例。 经分析与实践,总结出以下几种形式: 形式1:表达式中有多个自增或自减操作符,但作用 于不同的变量。如x=2,y=1,z=0;z+=++x+++y。 这种表示形式不易理解,也会引发编译错误,是一种显性 二义性。 形式2:自增自减运算符在表达式中连续出现,但作用 自增、自减运算符定义及运算规则 自增(++)、自减(一一)是单目运算符,其作用 、是使变量自增,自减1,它有两种作用方式。一种是单目 运算符作用于变量左边,称为前缀运算。另一种是单目运 算符作用于变量之后,称作后缀运算。自增自减运算表达 式规则如下: 1.前置自增(++i)表示变量自增1,然后把其值 作为表达式的值,这就是前置自增的超前作用。前置自减 于同一变量。如i=3;t=++i+(++i)+(++i)。 在此例中不同系统会给出不同的解,以Tc++与Vc为例, t的值分别为l8和l6,这是一种隐性二义性,不易发现。 (一一i)表示变量自减,其它方面与自增完全相同。后置 自增(i++)表示把变量值先作为表达式的值,然后变量 自增l,这就是后置自增的滞后作用。同理后置自减(i一 )除表示变量自减外,其它方面与后置自增完全相同。 2.结合方向自右向左,有较高的优先级,且只能作用 于变量。它不仅能产生一个值,而且会改变内存中变量的 值。也正是由于其上述两个规则,带来了其副作用,它们 是产生二义性的诱因。 二、产生二义性的常见形式 一自增自减运算符定义简单,但对它的使用常带来二义 性问题。C++中的二义性主要分为三种,一种是显性二 义性,一种是隐性二义性,最后一种是理解二义性。显性 收稿日期:2oo7—O9—18 作者简介:张志强(1972一),男,山西文水人,太原师范学院计 算机系,讲师,硕士。 形式3:多参函数中参数以自增或自减形式出现。如a 6;printf(“%d,%d,%d”,a,++a,a一一)。在不同 系统下会有不同的结果,有的为6,7,7有的为6,6,6。 这也是一种隐性二义性,不易发现。 形式4:自增自减运算符只出现一次,但其作用的变量 在表达式中重复出现。如y—x++&&x。这会产生一种人 为理解的二义性,但编译器处理结果是一致的。 三、二义性现象的原因分析与解决策略 形式l所示的显性二义性主要由于表示方式不当引起 的。C++编译系统在处理运算符时是按照“尽可能多” 原则将若干个字符从左到右组成一个运算符,因此上例z +=++x+++y中显性二义性是由于其本身等价于z+ :++(x++)+y,即自增运算对x++子表达式进行 操作,这是不合法的,也是与我们的理解有冲突的。这种 显性二义性并不一定总能被编译器发现,具有较大的不安 全性。解决的方法是规范表示方法。如把上例改为z+= :・43・ 维普资讯 http://www.cqvip.com 山西广播电视大学学报 2008年第1期 (++x)+(++Y),这样编译器与读程者都不会产生歧 ANSI/IS0 C标准这样描述序列点:“在上一个和下一个序 义,同时满足了此类运算符只作用于变量的要求。 列点之间,一个对象所保存的值至多只能被表达式的计算 形式2所引发的隐性二义性不易发现但危害较大。产 修改一次。而且前一个值只能用于决定将要保存的值。”它 生的主要原因是自增自减运算符本身的副作用,它会导致 所映了序列点是一个时间点(在整个表达式全部计算完毕 存储在内存中的变量值发生改变,即不仅产生一个值而且 之后或在I f、&&、?:或逗号运算符处,或在函数调用 会修改内存中的变量值,这一点与其他运算符有明显不同。 之前),此刻尘埃落定,所有的副作用都已确保结束。 连续使用这类运算符对同一变量进行连续操作的方式,c 例如: ++标准没有明确作出规定,致使处理方法完全由C++ int X=0,y:1;x Y~一I I Y//由于I I会引入序 编译器的编写者决定,由于他们处理方法存在不一致,所 列点,自减运算的滞后作用在此位置终止,I I之后的Y 以运算结果受到影响。相似的例子如X一一一x一一,X= 受影响为0。 (X++)+1等。我们把这种问题称作未定义现象。它是 int 0,Y=1:i++?i:i+Y//由于:引入了序 产生隐l生二义性最主要的原因,由于编译器无法发现异常 列点,后置自增运算的滞后作用在此位置终止,:之后的i 问题,而各种编译器处理结果又不一致,因此编程者往往 受影响,因此表达式结果为2。 很难发现问题的根源。我们应尽量杜绝这类未定义现象的 intt=5;t十+,t%3//逗号表达式引入了序列点,值 出现,只有这样才能保证程序结果的一致。对例子i=3;f 为0。 =++i+(++i)+(++i)中的问题可采取如下方法 所有这些运算符在此处有一个特殊的例外:如果左边 来解决:++i;++i;t=++i这样不但各种编译器处理 的子表达式决定最终结果(即,I I、&&?:、,等),则 结果都是一致的,而且也便于读程者理解,这是良好的程 右边的子表达式不会计算,会引入一个额外的内部序列点。 序设计风格。 只要清楚把握序列点的概念,就不会产生理解上的二义性。 形式3产生的原因与形式2相似,各种编译器对参数 c++中自增自减运算符使用灵活,但不能滥用。对 的处理方向不同,c++标准没有明确作出规定,这将导 其使用应遵循一定的规则,否则会引发二义性问题,带来 致参数的入栈处理方向不同,有的从左到右,有的正好相 许多不安全因素,我们应对其高度认识。在教学实践中我 反。在a=6:pfinff(“%d,%d,%d”,a,++a,a一一) 们可以总结一些产生二义性的常见形式,剖析其引发的原 中出现的二义性正是由于这个原因引起的。但它产生的危 因并探讨解决这一问题的有效方法,从而使学习者对自增 害不大,问题主要是给程序员的理解造成一定程度的困难。 自减运算有更好地认识和应用,为避免二义性问题的出现 对这类问题的处理主要是明确编译器对参数人栈处理的方 提供理论和实践上的保障。 向。如把上例改为a=6;pfinff(“%d,%d,%d,%s”,a, 参考文献: ++a,a一一,printf“direction’’);这样可以根据direction [1]谭浩强.C程序设计[M].北京:清华大学出版社, 2005. 字符出现的顺序明确当前编译器对参数的处理方向,从而 [2]吕凤翥.C++程序设计[M].北京:清华大学出版社, 进行下一步的处理。 2oo3、 形式4不会引发编译器二义性,但常会给读程者的理 [3]戴特等.C++大学教程[M].北京:电子工业出版社, 解带来障碍,它一般出现在使用后置增减的表达式中。在 2003. 上例int x:0,Y=l;y—X++&&x中,会出现人为理解和 [4]德.Nicolai M.Josuttis.C++标准程序库自修教程与参 编译结果的不一致。读程者往往认为表达式的结果应该为 考手册[M].武汉:华中科技大学出版社,2002. [5]王晓晶.试论德育教学的重要性[J],山西广播电视大 0,而编译结果为1。出现这种问题的原因也正是由于后置 学学报,2007,(1):33—34. 自增的滞后作用。对这类问题的理解应掌握序列点的概念。 Ambiguity of Auto——increasing and Auto——decreasing Operators in C++Programming Zhang Zhiqiang (Computer Department,Taiyuan Normal University,Taiyuan,Shanxi,030012) Abstract:Auto—increasing and auto—decreasing operators are widely used.but they are also very complex and risky.Improper use of these operators may bring about ambiguity.This paper explores the possible causes that lead to side effects in the use of these operations and provides some solutions to ensure the security of their use. Key words:ambiguity;auto—increasing;auto—decreasing 本文责编安春娥 ・44・