编译系统对求值顺序都有明确规定,例如对“++”和“--”指令,不同的编译系统对它们的处理顺序不相同,称这类指令为依靠方向性的指令。
为了提高可靠性和可移植性,就要避免使用依靠编译系统的语法。所谓不依靠编译系统,就是指避免依靠方向性(如“++”指令)。编程时绝对不能偷懒,在需要明确计算方向的场合,一定要明确。这时可以用括号明确计算方向,或者改变编写方法。
下面的程序段就是使用了方向性指令。
i=0 ; while (i<n ) y[i] = x[i++] ;
在这个程序段中,“x[i++]”就是假设了求值的顺序。这在有些机器上可能是正常的,但在有些机器上则不一定。应该避免“x[i++]”这种写法,建议写成
i=0 ; while (i<n ) { y[i]=x[i] ; i++ ; }
的形式。也可以改变设计方法,例如,一般更建议写成
for ( i=0 ; i<n ; i++ ) y[i]=x[i] ;
的形式。
【例12.1】下面的程序用来求数组a两项之间的差值,试分析程序存在的问题。
#include <stdio.h> void main () { int i=0 ,j=0 ,a[6]={2 ,7 ,11 ,-45 ,88 ,43} ,b[6] ; while (b[i] !=0 ) { b[j++]=a[i++]-a[i] ; } for (i=0 ;i<5 ;i++ ) printf (\"b[%d]=%d \" ,i ,b[i] ); printf (\"n\" ); }
【分析】在执行语句
b[j++]=a[i++]-a[i] ;
的时候,主观上以为用i作为第1个值,递加i作为第2个值。其实并非如此,在一个运算表达式中,它们变成同一个下标,所以计算的值均为0。
由此可见,编译器能决定一些分支程序的执行顺序,所以执行结果具有系统和编译器的双重依赖性,是不好的编程方法。
像“++”和“--”之类的运算符,应该单列一行。必要时还要显式地限制运算顺序。
修改后的程序取消了变量j,都利用i计算,简单明了。
#include <stdio.h> void main () { int i=0 ,j=0 ,a[6]={2 ,7 ,11 ,-45 ,88 ,43} ,b[6] ; while (a[i] !=0 ) { b[i]=a[i] ; i++ ; b[i-1]=b[i-1]-a[i] ; } for (i=0 ;i<5 ;i++ ) printf (\"b[%d]=%d \" ,i ,b[i] ); printf (\"n\" ); }
运算结果如下:
b[0]=-5 b[1]=-4 b[2]=56 b[3]=-133 b[4]=45
在书写程序时,也要注意检查依赖系统的语句。例如“++”的写法是否正确。在某些场合下,++i和i++的效果一样,例如在for循环语句
for ( i=0 ; i<100 ; ++i )
中,将它写成++i和i++都是可以的(尽管在效果一样的情况下,推荐前置写法),但在某些场合就不能随意交换。
【例12.2】写法效果不一样的例子。
#include <stdio.h> int main () { int x , y , i=2 , j=2 ; y = 2 + ++i ; x = 2+ j++ ; printf (\"y=%d , x=%d , i=%d , j=%dn\" , x , y , i , j ); return 0 ; }
程序运行结果如下:
y=4 , x=5 , i=3 , j=3
一定要注意运算表达式的顺序。如果怕混淆,干脆采取给定顺序的写法。例如将程序写成如下形式,运行结果一样。
【例12.3】避免误解的例子。
#include <stdio.h> int main () { int x , y , i=2 , j=2 ; y = 2 + i ; ++i ; j++ ; x = 2+ j ; printf (\"y=%d , x=%d , i=%d , j=%dn\" , x , y , i , j ); return 0 ; }
当然,可以用“i=i+1”替代“++i”。但两者是有区别的,在“++i”执行中i只计算一次,而在“i=i+1”执行中i要计算两次。C语言之所以提供增量和减量运算符,不仅是为了程序书写方便,也是考虑到编译器的效率。一般硬件CPU都提供了增量和减量指令,因此增减运算可以直接采用这些指令实现,从而提高程序效率。
需要注意的是,老版本的C编译器会将“a=-5;”理解为“a=a-5;”,而不是“a=(-5)”,所以建议对可能产生问题的地方均予以回避。例如VC6.0把如下语句
a=b/*p ;
中的“*”号作为注释,出现绿色字体。必须在“/”与“*”之间留有空格。改为
a= b/ *p
或者
a= b/ (*p );
当然也要避免写作
a=/*b ;
有些属于准两义性错误,编译器检查不出来,有时会造成严重的错误。
对于这类运算符,也要注意表达式的正确性。例如下面求和循环语句
for (i=1 ; i<=100 ; ++i ) sum += sum+i ; printf (\"sum=%d\" , sum );
输出sum=-102而不是5050,就是将“sum+=i;”错为“sum+=sum+i;”造成的。有时可以用显式表述以避免错误,如“sum=sum+i;”。