讀古今文學網 > 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;」。