首页 » C语言解惑 » C语言解惑全文在线阅读

《C语言解惑》12.2 克服依靠编译系统产生的错误

关灯直达底部

编译系统对求值顺序都有明确规定,例如对“++”和“--”指令,不同的编译系统对它们的处理顺序不相同,称这类指令为依靠方向性的指令。

为了提高可靠性和可移植性,就要避免使用依靠编译系统的语法。所谓不依靠编译系统,就是指避免依靠方向性(如“++”指令)。编程时绝对不能偷懒,在需要明确计算方向的场合,一定要明确。这时可以用括号明确计算方向,或者改变编写方法。

下面的程序段就是使用了方向性指令。


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;”。