[英]Why do logical operators in C not evaluate the entire expression when it's not necessary to?
我正在為我的計算機體系結構課閱讀我的教科書,我發現了這個說法。
邏輯運算符“
&&
”和“||
”之間的第二個重要區別 ' 與它們的位級對應物 '&
' 和 '|
' 是如果表達式的結果可以通過評估第一個參數來確定,則邏輯運算符不評估它們的第二個參數。 因此,例如,表達式a && 5/a
永遠不會導致被零除,表達式p && *p++
永遠不會導致取消引用空指針。 (Computer Systems: A Programmer's Perspective by Bryant and O'Hallaron,第 3 版,第 57 頁)
我的問題是為什么 C 中的邏輯運算符的行為如此? 使用作者的a && 5/a
示例,C 是否不需要評估整個表達式,因為&&
要求兩個謂詞都為真? 不失一般性,我同樣的問題適用於他的第二個例子。
短路是一種性能增強,碰巧可用於其他目的。
您說“C 是否不需要計算整個表達式,因為&&
要求兩個謂詞都為真?” 但想想吧。 如果&&
的左側為假,那么右側的計算結果是否重要? false && true
或false && false
,結果都是一樣的:false。
因此,當確定&&
的左側為假時,或確定||
的左側時確定為true,右邊的值無所謂,可以跳過。 這消除了評估潛在昂貴的第二次測試的需要,從而使代碼更快。 想象一下,如果右側調用一個函數來掃描整個文件以獲得給定的字符串? 如果第一個測試意味着您已經知道組合答案,您是否不希望跳過該測試?
C 決定超越保證短路到保證評估順序,因為這意味着像您提供的那樣的安全測試是可能的。 只要測試是冪等的,或者僅在不短路時才會出現副作用,則需要此功能。
一個典型的例子是空指針檢查:
if(ptr != NULL && ptr->value) {
....
}
如果沒有短路評估,這將在取消引用空指針時導致錯誤。
程序首先檢查左部分ptr != NULL
。 如果計算結果為false
,則不必計算第二部分,因為很明顯結果將為false
。
在表達式X && Y
,如果X
的計算結果為false
,那么我們知道X && Y
將始終為false
,無論Y
的值是多少。 因此,無需評估Y
。
在您的示例中使用此技巧來避免除以0
。 如果a
被評估為false
(即a == 0
),那么我們不評估5/a
。
它還可以節省大量時間。 例如,在評估f() && g()
,如果對g()
的調用f() && g()
並且f()
返回false
,則不評估g()
是一個很好的特性。
C 是否不需要計算整個表達式,因為 && 要求兩個謂詞都為真?
答案:不。為什么在“已經”知道答案的情況下還要工作?
根據邏輯 AND 運算符的定義,引用C11
,第 6.5.14 章
&&
運算符在其兩個操作數比較不等於 0時應產生1
; 否則,結果為 0。
按照這個類比,對於a && b
形式的表達式,如果a
的計算結果為 FALSE,無論b
的計算結果如何,結果都將為 FALSE。 無論如何,無需浪費機器周期檢查b
然后返回 FALSE。
邏輯 OR 運算符也是如此,如果第一個參數的計算結果為 TRUE,則返回值條件已經找到,不需要計算第二個參數。
這只是規則,非常有用。 也許這就是規則的原因。 這意味着我們可以編寫更清晰的代碼。 另一種選擇 - 使用if
語句會產生更冗長的代碼,因為您不能直接在表達式中使用if
語句。
你已經舉了一個例子。 另一個類似於if (a && b / a)
以防止整數除以零,其行為在 C 中未定義。這就是作者在編寫a && 5/a
保護自己a && 5/a
。
偶爾如果你總是需要評估兩個參數(也許它們調用有副作用的函數),你總是可以使用&
和|
.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.