3. 布爾代數

第 1 節 “if語句”講過,a<b<c不表示b既大於a又小於c,那麼如果想表示這個含義該怎麼寫呢?可以這樣:

if (a < b) {
	if (b < c) {
		printf("b is between a and c.\n");
	}
}

我們也可以用邏輯與(Logical AND)運算符表示這兩個條件同時成立。邏輯與運算符在C語言中寫成兩個&號(Ampersand),上面的語句可以改寫為:

if (a < b && b < c) {
	printf("b is between a and c.\n");
}

對於a < b && b < c這個控製表達式,要求“a < b的值非0”和“b < c的值非0”這兩個條件同時成立整個表達式的值才為1,否則整個表達式的值為0。也就是只有兩個條件都為真,它們做邏輯與運算的結果才為真,有一個條件為假,則邏輯與運算的結果為假,如下表所示:

表 4.2. AND的真值表

ABA AND B
000
010
100
111

這種表稱為真值表(Truth Table)。注意邏輯與運算的操作數以非0表示真以0表示假,而運算結果以1表示真以0表示假(類型是int),我們忽略這些細微的差別,在表中全部以1表示真以0表示假。C語言還提供了邏輯或(Logical OR)運算符,寫成兩個|綫(Pipe Sign),邏輯非(Logical NOT)運算符,寫成一個!號(Exclamation Mark),它們的真值表如下:

表 4.3. OR的真值表

ABA OR B
000
011
101
111

表 4.4. NOT的真值表

ANOT A
01
10

邏輯或表示兩個條件只要有一個為真,它們做邏輯或運算的結果就為真,只有兩個條件都為假,邏輯或運算的結果才為假。邏輯非的作用是對原來的邏輯值取反,原來是真的就是假,原來是假的就是真。邏輯非運算符只有一個操作數,稱為單目運算符(Unary Operator),以前講過的加減乘除、賦值、相等性、關係、邏輯與、邏輯或運算符都有兩個操作數,稱為雙目運算符(Binary Operator)

關於邏輯運算的數學體系稱為布爾代數(Boolean Algebra),以它的創始人布爾命名。在編程語言中表示真和假的數據類型叫做布爾類型,在C語言中通常用int型來表示,非0表示真,0表示假[6]。布爾邏輯是寫程序的基本功之一,程序中的很多錯誤都可以歸因于邏輯錯誤。以下是一些布爾代數的基本定理,為了簡潔易讀,真和假用1和0表示,AND用*號表示,OR用+號表示(從真值表可以看出AND和OR運算確實有點像乘法和加法運算),NOT用¬表示,變數xyz的值可能是0也可能是1。

¬¬x=x

x*0=0
x+1=1

x*1=x
x+0=x

x*x=x
x+x=x

x*¬x=0
x+¬x=1

x*y=y*x
x+y=y+x

x*(y*z)=(x*y)*z
x+(y+z)=(x+y)+z

x*(y+z)=x*y+x*z
x+y*z=(x+y)*(x+z)

x+x*y=x
x*(x+y)=x

x*y+x*¬y=x
(x+y)*(x+¬y)=x

¬(x*y)=¬x+¬y
¬(x+y)=¬x*¬y

x+¬x*y=x+y
x*(¬x+y)=x*y

x*y+¬x*z+y*z=x*y+¬x*z
(x+y)*(¬x+z)*(y+z)=(x+y)*(¬x+z)

除了第1行之外,這些公式都是每兩行一組的,每組的兩個公式就像對聯一樣:把其中一個公式中的*換成+、+換成*、0換成1、1換成0,就變成了與它對稱的另一個公式。這些定理都可以通過真值表證明,更多細節可參考有關數字邏輯的教材,例如[數字邏輯基礎]。我們將在本節的練習題中強化訓練對這些定理的理解。

目前為止介紹的這些運算符的優先順序順序是:!高於* / %,高於+ -,高於> < >= <=,高於== !=,高於&&,高於||,高於=。寫一個控製表達式很可能同時用到這些運算符中的多個,如果記不清楚運算符的優先順序一定要多套括號。我們將在第 4 節 “運算符總結”總結C語言所有運算符的優先順序和結合性。

習題

1、把代碼段

if (x > 0 && x < 10);
else
	printf("x is out of range.\n");

改寫成下面這種形式:

if (____ || ____)
	printf("x is out of range.\n");

____應該怎麼填?

2、把代碼段:

if (x > 0)
	printf("Test OK!\n");
else if (x <= 0 && y > 0)
	printf("Test OK!\n");
else
	printf("Test failed!\n");

改寫成下面這種形式:

if (____ && ____)
	printf("Test failed!\n");
else
	printf("Test OK!\n");

____應該怎麼填?

3、有這樣一段代碼:

if (x > 1 && y != 1) {
	...
} else if (x < 1 && y != 1) {
	...
} else {
	...
}

要進入最後一個else,x和y需要滿足條件____ || ____。這裡應該怎麼填?

4、以下哪一個if判斷條件是多餘的可以去掉?這裡所謂的“多餘”是指,某種情況下如果本來應該打印Test OK!,去掉這個多餘條件後仍然打印Test OK!,如果本來應該打印Test failed!,去掉這個多餘條件後仍然打印Test failed!

if (x<3 && y>3)
	printf("Test OK!\n");
else if (x>=3 && y>=3)
	printf("Test OK!\n");
else if (z>3 && x>=3)
	printf("Test OK!\n");
else if (z<=3 && y>=3)
	printf("Test OK!\n");
else
	printf("Test failed!\n");


[6] C99也定義了專門的布爾類型_Bool,但目前沒有被廣泛使用。