当前位置:嗨网首页>书籍在线阅读

18-不要自作聪明

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

5.3.6 不要自作聪明

如果一次用太多递增运算符,自己都会糊涂。例如,利用递增运算符改进 squares.c 程序(程序清单5.4),用下面的 while 循环替换原程序中的 while 循环:

while (num < 21)
     {
     printf("%10d %10d\n", num, num*num++);
     }

这个想法看上去不错。打印 num ,然后计算 num num 得到平方值,最后把 num 递增 1 。但事实上,修改后的程序只能在某些系统上能正常运行。该程序的问题是:当 printf() 获取待打印的值时,可能先对最后一个参数( `numnum++)求值,这样在获取其他参数的值之前就递增了num` 。所以,本应打印:

5        25

却打印成:

6        25

它甚至可能从右往左执行,对最右边的 num++ 作用的 num )使用 5 ,对第2个 num 和最左边的 num 使用 6 ,结果打印出:

6        30

在C语言中,编译器可以自行选择先对函数中的哪个参数求值。这样做提高了编译器的效率,但是如果在函数的参数中使用了递增运算符,就会有一些问题。

类似这样的语句,也会导致一些麻烦:

ans = num/2 + 5*(1 + num++);

同样,该语句的问题是:编译器可能不会按预想的顺序来执行。你可能认为,先计算第1项( num/2 ),接着计算第2项( 5 * (1 + num++) )。但是,编译器可能先计算第2项,递增 num ,然后在 num/2 中使用 num 递增后的新值。因此,无法保证编译器到底先计算哪一项。

还有一种情况,也不确定:

n = 3;
y = n++ + n++;

可以肯定的是,执行完这两条语句后, n 的值会比旧值大 2 。但是, y 的值不确定。在对 y 求值时,编译器可以使用 n 的旧值( 3 )两次,然后把 n 递增 1 两次,这使得 y 的值为 6n 的值为 5 。或者,编译器使用 n 的旧值( 3 )一次,立即递增 n ,再对表达式中的第2个 n 使用递增后的新值,然后再递增 n ,这使得 y 的值为 7n 的值为 5 。两种方案都可行。对于这种情况更精确地说,结果是未定义的,这意味着C标准并未定义结果应该是什么。

遵循以下规则,很容易避免类似的问题:

  • 如果一个变量出现在一个函数的多个参数中,不要对该变量使用递增或递减运算符;
  • 如果一个变量多次出现在一个表达式中,不要对该变量使用递增或递减运算符。

另一方面,对于何时执行递增,C还是做了一些保证。我们在本章后面的“副作用和序列点”中学到序列点时再来讨论这部分内容。