2011-10-10 103 views
3

我碰到這樣的代碼:&&和||運營商

int main() 
    { 
     int i=1,j=2,k=0,m=0; 
     m = ++i || ++j && ++k; 
     printf("%d %d %d %d %d",i,j,k,m); 
    } 

程序返回2 2 0 1...。爲什麼?

&&||具有更高的優先級,因此++j && ++k應該先評估。因此我期望j=3k=1。它將返回true,因此||成爲真,因此++i不應被評估。但它可以用其他方式工作。

我想別人向我解釋。

+20

哦,不,不會再 –

+0

這是一個常見的問題功課這些天? – crashmstr

回答

11

具有較高的優先級並不意味着它就會先計算。這只是意味着它更緊密。在該示例中,該表達式等同於:++i || (++j && ++k)。首先評估的是++i,因爲||從左到右評估。只有在評估結果爲false時纔會評估++j && ++k,因爲||是短路。

+3

's/priority/precedence /' –

9

其實++i將首先評估。只有當它是假的時候纔會對右側進行評估(在你的情況下不是)。

「& &具有更高優先級」這一事實涉及優先級(操作數緊貼它)而不是「其操作數首先得到評估」。

由於&&確實上表||,表達將被解釋如下:

m = ++i || (++j && ++k) 
0

因爲||和& & short circuit,因此指定sequence points

注:這是最初標記C++以及,你會得到一個稍微不同的答案有作爲重載運算符||或& &不要短路只是內置那些

2

基本上||手段,「如果你收到了一些東西,是真的,返回,否則,返回無論事後發生的情況。」所以,唯一評估的是m = (++i != 0)。這意味着「增加i,將m分配給與0相比的值,打破」。

更具體地講,這是發生了什麼事:

i = 1; 
i = i + 1; 
if(i) { 
    m = 1; 
} 
else { // who cares, this will never happen. 
    j = j + 1; 
    if(j) { 
     k = k + 1; 
     m = (k != 0); // 1 
    } 
    else { 
     m = 0; 
    } 
} 
4

Short circuit evaluation。如果&&的左側不爲零,則只有在那時纔會評估右側。同樣,只有當||的左側爲零時,纔會評估右側。

0

快捷鍵運算符將導致不必要的表達式組件不被評估。由於& &具有更高的優先級,因此如果您希望允許||,則需要最後對其進行評估。運算符能夠在++計算結果爲true時快捷整個表達式。既然是這樣,++ i是在「=」之後評估的唯一變量。

4

「較高的運算符優先級」與「首先評估的」不一樣。當您使用短路操作時,它們從左到右進行評估。任何算術運算的結果都會受到運算符優先級的影響,但這並不會改變短路的左t0右排序。

你的例子的複雜性是不這樣做的一個很好的理由。即使你知道規則並確切知道它會做什麼,下一位編程人員來看看代碼可能不會。

0

您在這裏看到邏輯運算符短路。如果||條件的第一部分爲真,那麼它永遠不會評估表達式的其餘部分(因爲如果第一部分是指針非空檢查,那麼如果第二部分中的指針爲空,則不想取消引用第二部分中的指針)。此外,由於它在布爾結果表達式中,所以++i的結果在被分配到m之前被轉換回bool1

避免像瘟疫這樣的代碼,它只會給你短期和長期調試噩夢。

0

評價的先後順序和順序是兩回事。 ||&&都從左到右評估它們的操作數;優先權不會改變這一點。

鑑於表達式a || b && c,將首先評估a。如果結果爲0,則將評估b && c

0

比較運算符的順序(||和& &)更重要。這就是爲什麼你最好先放置你最重要的測試。

1

在C語言中,有你需要知道的兩個不同的問題:運算符優先級爲了評估的。

運算符優先級決定哪個運算符獲得首先評估的操作數,以及屬於哪個運算符的哪些操作數。例如在表達式a + b * c中,運算符*的運算符優先級高於+。因此,表達式將被評估爲

a + (b * c)

在C語言中的所有運營商有確定性優先級,他們是在任何編譯器是相同的。

評估順序決定哪個操作數首先得到評估。請注意,子表達式也是一個操作數。評估順序在運營商優先級確定後應用。如果上面的a + b * c示例具有從左到右的評估順序,則操作數本身將按照a,b,c的順序進行評估。如果操作數是例如函數調用,那麼函數a(),b()和c()將按照該順序執行。這個例子中的所有操作數都需要被評估,因爲它們都被使用了。如果編譯器可以確定某些操作數不需要進行評估,則可以優化它們,而不管這些操作數是否包含副作用(如函數調用)。

的問題是操作數的計算順序是最常見的不確定的行爲,這意味着編譯器是免費的評估是左到右或從右到左,我們無法知道或承擔任何事它。對於C中的大多數操作數來說,這是事實,除了評估順序始終是確定性的一些例外。這些是運營商|| && ?: ,的操作數,其中評估的順序保證爲從左到右。 (當使用正規的C語言的語義,一個說有左的評價和右操作間的時序點。)


所以對於具體的例子m = ++i || ++j && ++k

  • 一元前綴++運算符的優先級最高,它們將運算符i,j和k綁定到它們。這個語法非常直觀。
  • 二進制& &運算符具有第二高的優先級,將運算符++j++k綁定到它。所以表達式相當於m = ++i || (++j && ++k)
  • 二進制||運營商具有第三高優先級,將運營商i++(j++ && ++k)=綁定到它。
  • 賦值操作符=具有最低的優先級,結合運營商m++i || (++j && ++k)到它。

此外,我們可以看到,無論是||並且運營商是保證評估順序保持從左到右的那些運營商之一。換句話說,如果||的左操作數運算符被評估爲true,編譯器不需要評估正確的操作數。在具體的例子,++i總是正的,所以編譯器可以執行相當的優化,有效地改造了表達m = ++i;

如果i值在編譯時不知道,編譯器會被強行評估整個表達。然後表達式將按此順序進行了評價:

  • & &比||優先級高,所以開始評估& &操作。
  • & &操作者保證具有左到右操作數的評價順序,因此首先執行++Ĵ。
  • 如果++ j的結果爲真(大於零),並且只有這樣,則評估正確的運算符:perform ++ k。
  • 商店++Ĵ& & ++ K的臨時變量的結果。我在這裏將其稱爲j_and_k。如果++ j和++ k都爲正,則j_and_k將包含值1(真),否則包含0(假)。
  • ||運算符保證有操作數從左到右的評估順序,所以先執行++ i。
  • 如果++ i是假(零),也只有這樣,評估右操作「j_and_k」。如果其中一個或兩個都是肯定的,則||的結果運算符爲1(真),否則爲0(假)。
  • m取決於結果獲取值1或0。