映月读书网 > C语言解惑 > 12.2 克服依靠编译系统产生的错误 >

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