编写高质量代码:改善C++程序的150个建议(连载3)

发表于:2012-4-05 09:38

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:李健    来源:51Testing软件测试网采编

  建议3:对表达式计算顺序不要想当然

  一条一条的表达式构成了C/C++代码的主体。接下来我们就来说说表达式的计算顺序。这些都是很琐碎的事情,但不可否认却又是非常有价值的。也许你会觉得下面的代码片段很熟悉:

  1. if( nGrade & MASK == GRADE_ONE )  
  2.     ... // processing codes

  很明显,当grade等于GRADE_ONE 时if条件成立才是程序员的本意。可是上面的代码并没有正确地表达程序员的意思。这是因为位运算符(&和| )的优先级低于关系运算符(比如==、<、>),所以上述代码的真实效果是:

  1. if( nGrade & (MASK == GRADE_ONE) )  
  2.     ... // processing codes

  这是很多人都容易犯的错误,我也有类似的经历,想当然地认为程序会按照设想的顺序来执行。这样的错误是很难发现的,调试起来也相当的费劲。C++/C语言的运算符多达数十个,而这数十个运算符又具有不同的优先级与结合律,熟记它们确实比较困难,不过,可以用括号把意图表示得更清楚,所以不要吝啬使用括号,即使有时并不必要:

  1. if( (nGrade & MASK) == GRADE_ONE )  
  2.     ... // processing codes

  这样,代码就没有了歧义。

  C/C++语言中存在着“相当险恶”的优先级问题,很多人很容易在这方面犯错误。如果代码表达式中包含较多的运算符,为了防止产生歧义并提高可读性,那么可以用括号确定表达式中每一个子表达式的计算顺序,不要过分自信地认为自己已经熟悉了所有运算符的优先级、结合律,多写几个括号确实是个好主意。例如:

  1. COLOR rgb = (red<<16) | (green<<8) | blue;  
  2. bool isGradeOne = ((nGrade & MASK) == GRADE_ONE);

  上面所说的计算顺序其实就是运算符的优先级,它只是一个“开胃菜”。接下来要说的是最为诡异的表达式评估求值的顺序问题。

  因为C++与C语言之间“剪不断理还乱”的特殊关系,C语言中的好多问题也被带入到C++的世界里了,包括表达式评估求值顺序问题。在C语言诞生之初,处理器寄存器是一种异常宝贵的资源;而复杂的表达式对寄存器的要求很高,这使得编译器承受着很大的压力。为了能够使编译器生成高度优化的可执行代码,C语言创造者们就赋予了寄存器分配器这种额外的能力,使得它在表达式如何评估求值的问题上留有很大的处理余地。虽然当今寄存器有了极大的进步,对复杂表达式的求值不再有什么压力,但是赋予寄存器分配器的这种能力却一直没有收回,所以在C++中评估求值顺序的不确定性仍然存在,而且这很大程度上决定于你所使用的编译器。这就要求软件工程师更加认真仔细,以防对表达式设定了无依据、先入为主的主观评估顺序。

  这其实也是C语言的陷阱之一,《The C Programming Lauguage》(程序员亲切地称此书为“K&R”)中反复强调,函数参数也好,某个操作符中的操作数也罢,表达式求值次序是不一定的,每个特定机器、操作系统、编译器也都不一样。就像《The C Programming Language》影印版第2版的52页所说的那样:

  如同大多数语言一样,C语言也不能识别操作符的哪一个操作数先被计算(&&、||、?:和,四种操作符除外),例如x=f()+g()。

  这里所说的求值顺序主要包括以下两个方面:

  函数参数的评估求值顺序

  分析下面代码片段的输出结果:

  1. int i = 2010;  
  2. printf("The results are: %d %d", i, i+=1 );

  函数参数的评估求值并没有固定的顺序,所以,printf()函数的输出结果可能是2010、2011,也可能是2011、2011 。

  类似的还有:

printf("The results are: %d %d", p(), q() );

  p()和q()到底谁先被调用,这是一个只有编译器才知道的问题。

  为了避免这一问题的发生,有经验的工程师会保证凡是在参数表中出现过一次以上的变量,在传递时不改变其值。即使如此也并非万无一失,如果不是足够小心,错误的引用同样会使努力前功尽弃,如下所示:

  1. int para = 10;  
  2. int &rPara = para;  
  3. int f(int, int);  
  4. int result = f(para, rPara *= 2);

31/3123>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号