簡體   English   中英

在C ++中,為什么真正的&& true || false && false == true?

[英]In C++, why does true && true || false && false == true?

我想知道是否有人知道編譯器解釋以下代碼的方式:

#include <iostream>
using namespace std;

int main() {
 cout << (true && true || false && false) << endl; // true
}

這是真的,因為&&的優先級高於|| 或因為|| 是一個短路運算符(換句話說,短路運算符是否忽略所有后續表達式,或只是下一個表達式)?

&&的優先級高於||

Caladain有正確的答案,但我想回答你對他的回答的一個評論:

如果短路|| 運算符發生並使第二個&&表達式的執行短路,這意味着|| 運算符在第二個&&運算符之前執行。 這意味着&&和||從左到右執行 (不是&&優先權)。

我認為你遇到的部分問題是,優先級並不意味着你認為它意味着什么。 &&優先級高於|| ,這恰恰說明了你所看到的行為。 考慮普通算術運算符的情況:假設我們有a * b + c * (d + e) 優先級告訴我們的是如何插入括號:首先圍繞* ,然后圍繞+ 這給了我們(a * b) + (c * (d + e)) ; 在你的情況下,我們有(1 && 1) || (infiniteLoop() && infiniteLoop()) (1 && 1) || (infiniteLoop() && infiniteLoop()) 然后,想象成為的表達。 為此,將每個運算符轉換為具有兩個參數作為子節點的節點:

表達樹。

評估這個樹是短路的地方。在算術樹中,你可以想象一個廣度優先的自下而上的執行風格:首先評估DE = d + e ,然后AB = a * bCDE = c * DE ,最終結果是AB + CDE 但請注意,你可以等效地評價AB ,再DECDE ,最終的結果; 你無法分辨出來。 但是,因為|| &&是短路的,他們必須使用這個最左邊的第一次評估。 因此,要評估|| ,我們首先評估1 && 1 因為這是真的, || 短路而忽略其右支路-盡管, 如果它評估了它,就不得不評估infiniteLoop() && infiniteLoop()第一。

如果它有幫助,您可以將樹中的每個節點視為函數調用,在第一種情況下產生以下表示plus(times(a,b), times(c,plus(d,e))) ,並且or(and(1,1), and(infiniteLoop(),infiniteLoop())在第二種情況下。短路意味着你必須完全評估每個左手函數參數orand ;如果它是true (對於or )或false (for and ),然后忽略右手參數。

您的評論預先假定我們首先評估具有最高優先級的所有內容,然后評估具有次高優先級的所有內容,依此類推,依此類推,這對應於樹的廣度優先自下而上執行。 相反,發生的事情是優先級告訴我們如何構建樹。 在簡單的算術情況下,樹的執行規則是無關緊要的; 然而,短路恰好是如何評估樹的精確規范。


編輯1:在你的其他一條評論中,你說

您的算術示例需要在最終添加之前評估兩個乘法,是不是定義優先級的是什么?

是的,這是定義優先級的原因 - 除非它不完全正確。 C中肯定是正確的,但考慮如何評估頭部中的(非C!)表達式0 * 5 ^ 7 ,其中5 ^ 7 = 5 7並且^具有比*更高的優先級。 根據你的廣度優先自下而上的規則,我們需要先評估05 ^ 7才能找到結果。 但你不會費心去評價5 ^ 7 ; 你只是說“好吧,因為所有x 0 * x = 0 ,這必須是0 ”,並跳過整個右手分支。 換句話說,在評估最終乘法之前,我沒有完全評估雙方; 我已經短路了。 同樣,因為false && _ == falsetrue || _ == true true || _ == true對於任何_ true || _ == true ,我們可能不需要觸摸右側; 這就是操作員短路的意義。 C不會短路乘法(雖然語言可以做到這一點),但它確實會短路&&||

正如短路0 * 5 ^ 7不會改變通常的PEMDAS優先規則一樣,短路邏輯運算符並不會改變&&優先級高於||的事實。 這只是一個捷徑。 由於我們必須首先選擇運算符的一側進行求值,因此C承諾首先評估邏輯運算符的左側; 一旦完成了這個,就會有一種明顯的(有用的)方法來避免評估某些值的右側,C承諾這樣做。

你的規則 - 評估表達式廣度 - 自下而上 - 也是明確定義的,語言可以選擇這樣做。 然而,它具有不允許短路的缺點,這是一種有用的行為。 但是如果樹中的每個表達式都是明確定義的(沒有循環)和純粹的(沒有修改變量,打印等),那么你就無法區分它們。 只有在這些奇怪的情況下,“和”和“或”的數學定義沒有涵蓋,短路甚至是可見的。

另外,請注意,通過優先考慮最左邊的表達式,短路的工作原理並不重要 可以定義一種語言⅋⅋ ,其中⅋⅋代表and\\\\代表|| ,但是0 ⅋⅋ infiniteLoop()1 \\\\ infiniteLoop()將循環,而infiniteLoop() ⅋⅋ 0infiniteLoop() \\\\ 1將分別為false和true。 這恰好對應於選擇首先評估右側而不是左側,然后以相同的方式進行簡化。

簡而言之:優先級告訴我們的是如何構建解析樹。 評估解析樹的唯一明智的命令就是那些表現得好像我們在明確定義的純值上自上而下地評估它(如你所願)的那樣。 對於未定義或不純的值,必須選擇一些線性順序。 1一旦選擇了線性順序,運算符一側的某些值可以唯一地確定整個表達式的結果( 例如0 * _ == _ * 0 == 0false && _ == _ && false == false ,或true || _ == _ || true == true )。 因此,您可以在不完成對線性順序之后發生的任何事情的評估的情況下離開; C承諾為邏輯運算符&&||執行此操作 通過從左到右的方式評估它們,而不是為了其他任何事情。 然而,由於優先級,但我們知道 true || true && false true || true && falsetrue而不是false :因為

  true || true && false
→ true || (true && false)
→ true || false
→ true

代替

  true || true && false
↛ (true || true) && false
→ true && false
→ false

1:實際上,我們理論上也可以並行地評估運算符的兩邊,但是現在這並不重要,對C來說當然不會有意義。這會產生更靈活的語義,但是會產生一些問題 -效果(什么時候發生?)。

(&& true || false && false)使用&&具有更高優先級進行評估。

TRUE && TRUE = True

FALSE && FALSE = False

True || False = True

更新:

1&&1||infiniteLoop()&&infiniteLoop()

為什么這在C ++中生成?

和以前一樣,讓它分開。 &&的優先級高於|| 和C ++中的布爾語句短路。

1 && 1 = True.

當bool值轉換為整數值時,則

false -> 0
true -> 1

該表達式計算此(true)&&(true)語句,該語句使||短路,從而阻止無限循環運行。 還有更多的編譯器Juju正在進行,所以這是一個簡單的情況視圖,這對於這個例子是足夠的。

在非短路環境中,該表達式將永遠掛起,因為OR的兩側將被“評估”而右側將掛起。

如果您對優先級感到困惑,那么如果||,這就是原始帖子中的評估方式 優先級高於&&:

1st.) True || False = True
2nd.) True && 1st = True
3rd.) 2nd && false = false
Expression = False;

我不記得它是從右到左,還是從左到右,但無論哪種方式,結果都是一樣的。 在你的第二篇文章中,如果|| 有更高的優勢:

1st.) 1||InfLoop();  Hang forever, but assuming it didn't
2nd.) 1 && 1st;
3rd.) 2nd && InfLoop(); Hang Forever

tl; dr:仍然優先考慮首先評估&&,但編譯器也會短路OR​​。 從本質上講,編譯組這樣的操作順序(簡單地認為, 放下叉子:-P)

1st.) Is 1&&1 True?
2nd.) Evaluate if the Left side of the operation is true, 
      if so, skip the second test and return True,
      Otherwise return the value of the second test(this is the OR)
3rd.) Is Inf() && Inf() True? (this would hang forever since 
      you have an infinite loop)

更新#2: “但是,這個例子證明&&沒有優先權,因為||在第二個&&之前被評估。這表明&&和||具有相同的優先級,並按從左到右的順序進行評估。”

“如果&&具有優先權,它將評估第一個&&(1),然后是第二個&&(無限循環)並掛起程序。由於這不會發生,因此&|在||之前不會被評估。”

我們將詳細介紹這些內容。

我們在這里談論兩個截然不同的事情。 優先級,它決定了操作順序,以及短路,這是一種節省處理器周期的編譯器/語言技巧。

讓我們首先介紹優先順序。 優先順序是“操作順序”的簡稱本質上,給出了這樣的陳述:1 + 2 * 3,在哪個階段應該對操作進行分組以進行評估?

數學明確地將操作的順序定義為賦予乘法優先於加法的優先級。

1 + (2 * 3) = 1 + 2 * 3
2 * 3 is evaluated first, and then 1 is added to the result.
* has higher precedence than +, thus that operation is evaluated first.

現在,讓我們轉換為布爾表達式:(&& = AND,|| = OR)

true AND false OR true

因此,C ++賦予AND優先於OR的優先級

(true AND false) OR true
true AND false is evaluated first, and then 
      used as the left hand for the OR statement

因此,只有優先級,(true && true || false && false)將按此順序操作:

((true && true) || (false && false)) = (true && true || false && false)
1st Comparison.) true && true
2nd Comparison.) false && false
3rd Comparison.) Result of 1st comparison || Result of Second

和我一起到目前為止? 現在讓我們進入Short Circuiting:在C ++中,布爾語句被稱為“短路”。 這意味着編譯器將查看給定語句a選擇“最佳路徑”進行評估。 舉個例子:

(true && true) || (false && false)
There is no need to evaluate the (false && false) if (true && true) 
equals true, since only one side of the OR statement needs to be true.
Thus, the compiler will Short Circuit the expression.  Here's the compiler's
Simplified logic:
1st.) Is (true && true) True?
2nd.) Evaluate if the Left side of the operation is true, 
      if so, skip the second test and return True,
      Otherwise return the value of the second test(this is the OR)
3rd.) Is (false && false) True? Return this value

如您所見,如果(true && true)被評估為TRUE,那么就不需要花費時鍾周期來評估if(false && false)是否為真。

C ++總是簡短的Circuts,但其他語言為所謂的“Eager”運算符提供機制。

以編程語言Ada為例。 在Ada中,“AND”和“OR”是“渴望”操作員......他們強制要評估所有內容。

在Ada中(true和true)OR(false和false)將在評估OR之前評估(true和true)和(false和false)。 Ada還為您提供了使用AND THEN和OR ELSE進行短路的能力,這將為您提供與C ++相同的行為。

我希望完全回答你的問題。 如果沒有,請告訴我:-)

更新3:上次更新,如果您仍然遇到問題,我會繼續發送電子郵件。

“如果發生||運算符的短路並使第二個&&表達式的執行短路,則意味着在第二個&&運算符之前執行了||運算符。這意味着對&&和||的從左到右執行(不是&&優先權。)“

我們來看看這個例子:

(false && infLoop()) || (true && true) = true (Put a breakpoint in InfLoop and it won't get hit)
false && infLoop() || true && true = true  (Put a breakpoint in InfLoop and it won't get hit)
false || (false && true && infLoop()) || true = false (infLoop doesn't get hit)

如果你說的是真的,InfLoop會在前兩個中被擊中。 您還會注意到第三個示例中也沒有調用InfLoop()。

現在,讓我們來看看:

(false || true && infLoop() || true);

Infloop被召喚! 如果OR具有比&&更高的優先級,那么編譯器將評估:

(false || true) && (infLoop() || true) = true;
(false || true) =true
(infLoop() || true = true (infLoop isn't called)

但是InfLoop被召喚了! 這就是為什么:

(false || true && infLoop() || true);
1st Comparison.) true && InfLoop() (InfLoop gets called)
2nd Comparison.) False || 1st Comp (will never get here)
3rd Comparison.) 2nd Comp || true; (will never get here)

Precendece ONLY僅設置操作分組。 在此,&&大於||。

true && false || true && true gets grouped as
(true && false) || (true && true);

然后 ,編譯器出現了,決定什么樣的順序應該在執行評估給它保存周期的最佳機會。

Consider: false && infLoop() || true && true
Precedence Grouping goes like this:
(false && infLoop()) || (true && true)
The compiler then looks at it, and decides it will order the execution in this order:
(true && true) THEN || THEN (false && InfLoop())

這是一個事實..我不知道如何證明這一點。 優先級由語言語法規則決定。 編譯器的優化由每個編譯器確定。有些比其他編譯器更好,但是所有人都可以自由地重新排序它認為合適的分組比較,以便為最快的執行提供“最佳”機會,並進行最少的比較。

&&確實有更高的優先權

兩個事實解釋了兩個例子的行為。 首先, &&的優先級高於|| 其次,兩個邏輯運算符都使用短路評估。

優先權常常與評估順序混淆,但它是獨立的。 只要最終結果正確,表達式可以按任何順序評估其各個元素。 通常對於某些運算符,這意味着可以在右邊的值(RHS)之前或之后評估左邊的值(LHS),只要在應用運算符本身之前評估兩者。

邏輯運算符具有特殊屬性:在某些情況下,如果一方評估特定值,則無論另一方的值如何,都知道運算符的值。 為了使這個屬性有用,C語言(以及擴展每個類C語言)已經指定了邏輯運算符來在RHS之前評估LHS,並且進一步僅評估RHS是否需要它的值來知道結果。運營商。

因此,假設通常定義為TRUEFALSETRUE && TRUE || FALSE && FALSE 從左側開始評估TRUE && TRUE || FALSE && FALSE 第一個TRUE不強制第一個&&的結果,因此評估第二個TRUE ,然后計算表達式TRUE && TRUE (為TRUE)。 現在, || 知道它的LHS。 更好的是,它的LHS強制了||的結果 要知道,所以它會跳過對整個RHS的評估。

完全相同的評估順序適用於第二種情況。 ||的RHS 是無關緊要的,它沒有被評估,也沒有調用infiniteLoop()

此行為是設計使然,並且很有用。 例如,您可以編寫p && p->next知道表達式永遠不會嘗試取消引用NULL指針。

“如果發生||運算符的短路並使第二個&&表達式的執行短路,則意味着在第二個&&運算符之前執行了||運算符。這意味着對&&和||的從左到右執行(不是&&優先權。)“

不完全的。

(xx && yy || zz && qq)

會像這樣評估:

  1. 檢查第一個操作員
  2. 評估xx && yy
  3. 檢查下一個操作員
  4. 如果下一個運算符是|| ,聲明的第一部分是真的,跳過其余部分。
  5. 否則,在||之后檢查下一個運算符 ,並評估它: zz && qq
  6. 最后,評估||

根據我的理解,C ++的設計使它在開始評估之前讀取內容。 畢竟,在我們的例子中,它不知道我們在||之后有第二次&&檢查 直到它讀入它,這意味着它必須在||讀取 在它到達第二個&& 因此,如果第一部分評估為true,則不會在||之后執行該部分 ,但如果第一部分評估為假,那么它將執行第一部分,讀入|| ,找到並評估第二部分,並使用第二部分的結果來確定最終結果。

關於你的編輯:不會評估infiniteLoop()因為true || (無論如何)總是如此。 使用true | (無論如何)如果應該執行什么。

關於true && true || infiniteLoop() && infiniteLoop() true && true || infiniteLoop() && infiniteLoop()示例,由於兩個特征的組合,兩個無限循環調用都沒有被評估:&&優先於||和|| 當左側為真時短路。

如果&&和|| 具有相同的優先級,評估必須像這樣:

((( true && true ) || infiniteLoop ) && infiniteLoop )
(( true || infiniteLoop ) && infiniteLoop )
=> first call to infiniteLoop is short-circuited
(true && infiniteLoop) => second call to infiniteLoop would have to be evaluated

但由於&&的優先權,評估實際上是這樣

(( true && true ) || ( infiniteLoop && infiniteLoop ))
( true || ( infiniteLoop && infiniteLoop ))
=> the entire ( infiniteLoop && infiniteLoop ) expression is short circuited
( true )

關於安德魯的最新代碼,

#include <iostream>
using namespace std;

bool infiniteLoop () {
    while (true);
    return false;
}

int main() {
    cout << (true && true || infiniteLoop() && infiniteLoop()) << endl; // true
}

短路評估意味着無法執行對infiniteLoop的調用。

然而,它以一種不正常的方式很有趣,因為C ++ 0x草案使無限循環 - 沒有做任何未定義的行為 這條新規則通常被認為是非常不受歡迎和愚蠢的,甚至是徹頭徹尾的危險,但它有點潛入選秀。 部分來自線程場景的考慮,一篇論文的作者認為它會簡化某些事情的規則 - 或者其他相當不相關的規則。

所以,用一個編譯器,它是對的C ++ 0x的不合格的“前沿”的計划可能終止, 一些結果,即使執行調用infiniteLoop 當然,有了這樣的編譯器,它也可能產生那種可怕的現象,鼻子守護進程......

令人高興的是,如上所述,短路評估意味着保證不會執行呼叫。

干杯&hth。,

由於和/或/ true / false非常類似於* / + / 1/0(它們在數學上是等效的 - ish),因此以下情況也是如此:

1 * 1 + 0 * 0 == 1

而且很容易記住......

所以,是的,它與優先權短路有關。 如果將布局操作映射到相應的整數操作,則對布爾操作的優先級相當容易。

這是一個順序說明:

  (true && true || false && false)
= ((true && true) || (false && false))  // because && is higher precedence than ||, 
                                        //   like 1 + 2 * 3 = 7  (* higher than +)
= ((true) || (false))
= true

但也要注意,如果是的話

(true || ( ... ))

然后右側沒有被評估,因此沒有調用任何函數,表達式將返回true

簡單的答案是&&優先於||。 此外,代碼不會執行,因為它不需要知道布爾表達式的結果。 是的,它是一個編譯器優化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM