讀古今文學網 > C語言解惑 > 4.1 控制流程運算容易出現的問題 >

4.1 控制流程運算容易出現的問題

程序控制語句分為兩類:控制選擇和控制循環。具體實現涉及關係表達式、條件表達式和邏輯表達式,關係表達式又可以用來構成邏輯表達式。

4.1.1 寫錯關係運算符

表4-1是C語言提供的6種關係運算符及其含義。

表4-1 C語言提供的6種關係運算符及其含義

它沒有「=>」和「=<」運算符。判斷錯誤的方法很簡單,一般是說大於(>)等於(=)或小於(<)等於(=),不會說成等於大於或等於小於,記住相等(=)是最後敘述,當然放在最後。同理推之,不等(!)於(=)的格式必然是「!=」。

講到等(=)於(=),顯然沒有停頓,所以在兩個等(=)符號之間不能有空格。其實,這4個運算符之間都不能有空格,否則就成了不存在的運算符。不過最容易出現的錯誤還是在兩個等號之間留一個空格。

【例4.1】下面的程序是對運算後的變量求和,找出其中的錯誤。


#include <stdio.h>
void main
()
{
     int i=2
,j=5
,k=6
,sum=1
;
     i=i<5
;
     k==j<0
;
     sum==i+j+k
;
     printf
(\"%d+%d+%d=%dn\"
,i
,j
,k
,sum
);
}
  

程序的輸出結果為


1+5+6=1
  

雖然編譯給出警告,但並不影響產生執行文件,只是結果有點出人意料。

因為關係運算符的優先級高於賦值運算符,所以語句「i=i<5;」中先計算「2<5」,其結果為真,即為1,由賦值運算符賦給i,得到「i=1」。

在6種關係運算符中,<、<=、>和>=運算符的優先級別相同,但高於==和!=。語句「k==j<0;」並沒有價值,因為程序中沒有用到這個結果。其原因正是運算符「<」的優先級比「==」的高,使得C語言先計算它,「5<0」不成立,其結果為0。然後再計算「6==0」,顯然也不成立,結果為0。雖然這條語句的結果為0,但並不影響k的值,k仍然為6。

對語句「sum==i+j+k;」而言,「i+j+k=1+5+6=12」。判斷「1==12」不成立。但這個結果並不影響sum的值,所以sum仍然為1。由此得到錯誤的輸出「1+5+6=1」。

由此可見,程序多半是將賦值運算符「=」錯認為關係運算符「==」。如果將這兩條語句改為如下語句:


k=j<0
;
sum=i+j+k
;
  

對於語句「k=j<0;」而言,「5<0」不成立,其結果為0。將結果賦給k,得到「k=0」。程序運行結果將變為:1+5+0=6。

由此可見,應該消除所有的警告語句。儘管有的程序能獲得正確結果,但也不能讓它存在此類信息。例如下面的程序。

【例4.2】程序語句錯誤、運行結果正確的例子。


#include <stdio.h>
void main
()
{
     int i=0
,sum=0
;
     for
(i==1
;i<11
;i++
)
          sum=sum+i
;
     printf
(\"sum=%dn\"
,sum
);
}
  

for循環中應該是「i=1」。這裡的錯誤雖然不影響運算結果,但程序的執行效率是不一樣的。因為對於「i==1」,使得循環從「i=0」開始,多運行一次。只是這次運算加的是「0」值,才使得結果也是「sum=55」。

4.1.2 混淆表達式和關係表達式的值

由上一小節可見,不能混淆表達式和關係表達式,以及關係表達式的值。

【例4.3】演示表達式書寫不規範的例子。


#include <stdio.h>
void main
()
{
     int a=4
,b=3
,c=2
,d
,e
;
     d=a>b>c
;
     e=a>
(b>c
);
     printf
(\"d=%d
,e=%dn\"
,d
,e
);
}
  

對於「d=a>b>c;」,「>」運算符是自左至右運算,所以先算「a>b」的值為1,再執行關係運算「1>c」,得值0賦給d。

語句「e=a>(b>c);」使用括號改變運算順序,先計算括號裡面的「3>2」,結果為1,再計算「4>1」,結果為1,賦給e。程序輸出結果為


d=0
,e=1
  

儘管程序編譯成功,但有警告信息。由此可見,編譯系統並不希望採用這種賦值方法。即使像「(a+8)>=(b+3);」這種形式,也是不喜歡的。各個編譯系統對此的反應可能不一樣,這裡使用VC6.0,該編譯系統認為對連續兩個情況的轉換存在不安全性。對於單純由表達式加「;」號構成的語句,認為這個結果沒有被實際使用,所以也給出警告信息。明白這一點,就好理解了。

結論:關係表達式的值本身沒有價值,只是用來構成合理的語句。最常用的是兩種:一種是直接提供給控制語句,以便根據這個值執行相應動作;另一種是用來構成邏輯表達式,邏輯表達式的值再作為控制語句的控制依據。

表達式可以是算術表達式、邏輯表達式、賦值表達式、字符表達式等。例如:


b
,a+b
,d-c
,x=5
,y=7
,\'d\'
,\'c\'
,a>b
,b>c
  

用關係運算符將兩個表達式連接起來的式子,稱為關係表達式。例如:


a>b
,a+b<=d-c
,(x=5
)>=
(y=7
),\'d\'
!=\'c\'
  

關係表達式的值是個邏輯值,即「真」和「假」。C語言沒有邏輯(bool)型數據類型,僅以1代表「真」,以0代表「假」。所以當把關係表達式的值賦給整數類型的變量時,編譯系統會給出警告,認為這種賦值缺少安全性。

也可以用關係運算符將兩個關係表達式連接起來構成新的關係表達式。


(a>b
)>
( b>c
)
  

若a=4,b=3,c=2,對於如下的表達式,則有:

a>b的值為「真」,表達式的值為1。

(a>b)==c-1的值為「真」(因為a>b值為1,等於c-1的值),表達式的值為1。

a-b<c的值為「真」,表達式的值為1。

d=b>c,d的值為1。

e=a>b>c,e的值為0。因為「>」運算符是自左至右運算,所以先算「a>b」的值為1,再執行關係運算「1>c」,得值0賦給e。

由分析可知,可以將例4.3的程序改寫為例4.4的形式,以消除警告信息。

【例4.4】表達式書寫規範的例子。


#include <stdio.h>
void main
()
{
     int a=4
,b=3
,c=2
,d
,e
;
     d=a>b
;      d=d>c
;
     e=b>c
;      e=a>e
;
     printf
(\"d=%d
,e=%dn\"
,d
,e
);
}
  

4.1.3 混淆邏輯表達式和邏輯表達式的值

【例4.5】這個程序的本意是讓f的值大於等於2,結果出乎意料之外,輸出為「f<2」,請分析原因並改正錯誤。


#include <stdio.h>
void main
()
{
     int a=4
,b=3
,c=2
,d
,e
,f
;
     d=a>b
;      d=d>c
;
     e=b>c
;      e=a>e
;
     f=a<b&&c>e+2
;
     if
(f>=2
)  printf
(\"f>=2n\"
);
     else      printf
(\"f<2n\"
);
}
  

因為算術運算符的優先級高,所以先做「e+2」運算,實際上右邊的表達式變為「c>3」,其結果為0。左邊「a<b」運算的結果為0,將0賦給f,導致輸出f<2。

將該語句改為


    f=
(a<b&&c>e
)+2
;
  

即可得到f=2,從而得到正確的輸出「f>=2」。

C語言提供的邏輯運算符只有如下3種。

&& 邏輯與

|| 邏輯或

! 邏輯非

含有邏輯運算符的表達式就是邏輯表達式。顯然,邏輯表達式的值應該是一個邏輯量「真」或「假」。C語言編譯系統在給出邏輯運算結果時,以數值1代表「真」,以0代表「假」。

邏輯運算符進行邏輯運算,當然運算符兩側參加運算的值應該是邏輯值,所以上節說的關係表達式可以作為參加邏輯運算的對象。其實,C語言邏輯運算符兩側的運算對象並不限於關係表達式,即它不但可以是數值0和1,或者是0和非0的整數,也可以是任何類型的數據,包括字符型、實型或指針型等。不過,邏輯運算要求將兩側的對象先轉化為邏輯值。也就是系統最終以0和非0來判定它們是屬於「真」還是「假」,然後參與邏輯運算。如果一個量不為0,則為「真」,用1代表它。如果為0,則為「假」,以0代表它。例如


\'a\'
&&\'b\'
  

的值為1(因為字母a和字母b的ASCII碼的值都不為0,都按「真」處理)。而


a<b&&c>e
;
  

的值則要先計算邏輯運算符兩邊的關係表達式的值。如果a=4,b=3,c=2,e=1,則「a<b」為0,而「c>e」為1,結果為0。其實,對於「&&」運算符,左邊的式子為0已經決定結果為0,沒有必要再計算右邊的關係表達式,在實際計算中,並不計算右邊的表達式。

在邏輯表達式的求解中,並不是所有的邏輯運算符都被執行,只是在必須執行下一個邏輯運算符才能求出表達式的解時,才執行該運算符。

邏輯運算符的優先級低於關係運算符,所以能保證在求出關係運算符表達式的值之後進行邏輯運算。

4.1.4 混淆邏輯運算符和位運算符

最容易混淆「&&」和「&」,「||」和「|」。前者是邏輯運算符,後者是位運算符。位運算符的優先級比邏輯運算符高。

【例4.6】將「<」和「>」錯認為移位運算符的例子。


#include <stdio.h>
void main
()
{
     int a=4
,b=3
,c=5
;
     if
(a<<b
)   printf
(\"%d<%dn\"
,a
,b
);
     else       printf
(\"%d>%dn\"
,a
,b
);
     if
(b>>c
)   printf
(\"%d>%dn\"
,b
,c
);
     else       printf
(\"%d<%dn\"
,b
,c
);
}
 

這個程序將「<」錯為「<<」,將「>」錯為「>>」,得到如下的錯誤輸出。


4<3
3<5
  

「<<」和「>>」是移位運算符,相當於乘除法運算。

4<<3=4*2 3=32,32不是0,輸出「4<3」。

3>>5=3*2 -5=0,值為0,用else輸出「3<5」。

將這兩個符號修改即可得到如下的正確輸出。


4>3
3<5
  

混淆「&&」和「&」,「||」和「|」的情況,將在後面舉例。