2013-12-18 46 views
2

我有一個if語句,它的正文中有一條語句,但有一個條件由幾個OR'd表達式組成。如果條件爲OR'd的評估

在執行過程中,如果OR'd條件中的第一個表達式爲真,第二個表達式是否仍會被評估?

即使每個表達式else的主體都是相同的else表達式OR'd或每個表達式形成單一條件會更好嗎?

e.g

if ((!x && y && z) || (x && !y && !z) || (x != y) { 
    foo(); 

//vs 

if (!x && y && z) 
    foo(); 
else if (x && !y && !z) 
    foo(); 
else if (x != y) 
    foo(); 

回答

5

沒有,如果一個或(和)左側的計算結果爲真(假),右側不評估。這就是所謂的短路,並且是C.

的一個非常著名的特徵

您可以通過

int f() { 
    printf("f!\n"); 
    return 1; 
} 

int g() { 
    printf("g!\n"); 
    return 0; 
} 

int main() { 
    if(f() || g()) 
     printf("hey\n"); 
} 

檢查這這將打印

f! 
hey 
2

第一個代碼是「好」,因爲當使用||時,首先返回true的評估將結束條件評估。如果你還希望評估該條款,使用單個|

+0

代碼的第二部分將不會有所不同。我會假設編譯器會優化它。作爲一個幫助:推薦使用按位運算符可能值得一些解釋 –

-3

號,雙邏輯運算符(& &和||)後,其他每個參數一個將評估,直到它找到一個滿意的結果。

在& &的情況下,將評估所有參數,直至找到false,在這種情況下表達式條件爲false,並且if代碼將不會被評估。

在||的情況下,參數將被評估,直到它找到一個真,在這種情況下表達式條件爲真,並且if代碼將被評估。

如果您想要評估表達式的所有元素,請使用&並|運營商。他們將創建一個變量,該變量將被表達式的所有元素進行「與」或「或」運算,並且只有該變量的最終值纔會在條件中使用。

結果是一樣的,但是使用&和|,您確定您的條件的所有表達式都將被評估。

+3

使用條件是一個壞主意。 '2&1 == 0'。 – Guido

1

簡短的回答:NO
Ç短路所有如果與||&&邏輯運算符的語句。這是沒有辦法的。其實:C has no eager operators

這個背後的推理是,如果你想要一個單獨的動作,以防1,2,3情況都是真的,那麼就不需要評估所有的情況,只要其中一個必須是真實的。換句話說,短路評估速度更快。
因此,您的第一個片段是更好的選擇,只要它不妨礙可讀性。

考慮以下(僞代碼):

if (is_alphabet_letter('a') || is_alphabet_letter('b') || is_alphabet_letter('c')) 
    puts("Found letter of the roman alphabet"); 

這會導致只有1調用is_alphabet_letter功能,而不是三人。至於最終的程序而言,上面的代碼中的計算結果爲:

if (true /*|| jibberish, don't care*/) 
    puts("..."); 

鑑於此:

if (is_alphabet_letter('a')) 
    puts(""); 
else if (is_alphabet_letter('b') 
    puts(""); 
else if (is_alphabet_letter('c') 
    puts(""); 

只是添加了混亂,而且會是在所有likelyhood-由編譯器進行優化,以類似更像是第一種說法。
這同樣適用於&&,順便說一句:

if (false && true && a_complex_function()) 

請問從未結果中a_complex_function一個電話,簡單,因爲第一個表達式的計算結果爲false,並檢查任何剩餘表達式因此毫無意義。 C只是繼續前進到else塊,或者到下一個語句。
這就是爲什麼在if塊選擇表達式的順序有時有所作爲:

if (a_complex_function() && false) 

總是會調用該函數。有時候,這可能是你想要的,但有時候並非如此,因此考慮將更復雜的表達式移動到if語句的末尾並沒有什麼壞處。

1

這兩種情況很可能產生相同的機器代碼,或者非常接近的東西。所以選擇哪一個是編碼風格的問題。

我會說,如果操作很複雜,以至於它們不會輕易放在一條線上,那麼if-else if就更好了。否則,如果操作有些簡單,||版本可能更喜歡。雖然這是相當主觀的。

關於這些運營商的內部運作的高級討論如下。如果你對這些事情不感興趣,你可能會停止閱讀。


然而,也有一些細節,使這兩個版本非常輕微的不同。在這兩種情況下,每個單獨的操作數都是使用類型平衡隱式提升的。但是在||的情況下,存在額外的隱式促銷發生:

0123'的結果與(x && !y && !z)的結果相平衡。最終,結果是具有最大轉換等級的操作數的類型。

重要嗎?我不明白在這個具體情況下會如何。但是讓我們說,我們有這樣的事情:

uint8_t x; 
uint8_t y; 
uint32_t z; 

然後我們有一個表達:

if((x && y) || (x && z)) 

// or 

if(x && y) 
{} 
else if(x && z) 
{} 

讓我們假設我們使用的是8位或16位CPU,其中int爲16位。

在如果其他情況下,會發生以下情況:

  • x和y均爲整數uint8_t晉升爲int,因爲它們是小的整數類型。
  • 表達式(int)x && (int)y是平衡的。 x && y根據類型int計算,結果爲int
  • 在表達式x && z,x是如上所述的整數提升,但不是z,它不是一個小整數類型並且仍然是uint32_t
  • 表達式(int)x && (uint32_t)z是平衡的,然後計算uint32_t類型。結果是uint32_t
  • 因此x && y總是在一個16位變量上計算,而x && z總是在一個32位變量上計算。

如果我們看||版本,也會發生同樣的情況。但是接下來我們有另外一種額外的平衡:(uint16_t)first_result || (uint32_t)second_result。這種平衡由於||運營商而得到強制執行。這意味着如果x==truey==true,那麼整個操作的結果將是uint32_t類型。

但在if-else版本中,x && y類型始終爲uint16_t||版本在x類型和z類型之間引入了隱蔽的緊密耦合。

無論編譯器是否能夠有效優化||場景,我都不知道。我期望它,但是由於編譯器不能對整個表達式的結果做出編譯時決定,因爲它不知道操作數是真是假,它可能只是最終全部執行它在32位類型上。如果我們有一個8位或者16位的CPU,這將是一個壞消息,它會非常低效地處理32位數。

+0

+1提及促銷和解釋如此之好:) – Toby