17

コンパイラが次のコードを解釈する方法を誰かが知っているかどうか知りたいです:

#include <iostream>
using namespace std;

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

&& は || よりも優先順位が高いため、これは本当ですか? または|| は短絡演算子ですか (つまり、短絡演算子は後続のすべての式を無視しますか、それとも次の式だけを無視しますか)?

4

12 に答える 12

35

&&より優先順位が高い||

于 2010-10-31T05:08:14.837 に答える
29

Caladainは正確に正しい答えを持っていますが、彼の答えに対するあなたのコメントの 1 つに返信したいと思います。

|| を短絡する場合 演算子が発生し、2 番目の && 式の実行をショートサーキットします。つまり、|| 演算子は、2 番目の && 演算子の前に実行されました。これは、&& および || の左から右への実行を意味します。(&& 優先ではありません)。

あなたが抱えている問題の一部は、優先順位があなたが考えていることを意味していないということだと思います. &&の優先順位が よりも高いのは事実で||あり、これはまさにあなたが見ている動作を説明しています。通常の算術演算子の場合を考えてみましょう: があるとしますa * b + c * (d + e)。優先順位が示すのは、括弧を挿入する方法です。最初に を囲み*、次に を囲み+ます。これにより、次のことがわかり(a * b) + (c * (d + e))ます。あなたの場合、私たちは持ってい(1 && 1) || (infiniteLoop() && infiniteLoop())ます。次に、表現がになると想像してください。これを行うには、各演算子を子として 2 つの引数を持つノードに変換します。

式ツリー。

このツリーの評価は、ショートサーキットの出番です。算術ツリーでは、幅優先のボトムアップ実行スタイルを想像できます。最初に を評価しDE = d + e、次にAB = a * bと を評価CDE = c * DEし、最終結果はAB + CDEです。しかし、最初に 、次に、、および最終結果を評価することも同様可能であることに注意してください。あなたは違いを見分けることができません。ただし、とは短絡しているため、この左端からの評価を使用する必要があります。したがって、 を評価するには、まず を評価します。これが真であるため、短絡して右側の分岐を無視します。たとえそれを評価したとしても、ABDECDE||&&||1 && 1||infiniteLoop() && infiniteLoop()最初。

それが役立つ場合は、ツリー内の各ノードを関数呼び出しと考えることができます。これにより、plus(times(a,b), times(c,plus(d,e)))最初のケースでは次の表現が生成or(and(1,1), and(infiniteLoop(),infiniteLoop())され、2 番目のケースでは次の表現が生成されます。ショートサーキットとは、左辺の各関数引数をororに完全に評価する必要があることを意味しますandtrue(for or) またはfalse(for )の場合andは、右側の引数を無視します。

あなたのコメントは、最初に優先順位が最も高いものすべてを評価し、次に優先順位が高いものすべてを評価することを前提としています。これは、ツリーの幅優先のボトムアップ実行に対応します。代わりに、優先順位がツリーの構築方法を教えてくれます。単純な算術演算の場合、ツリーの実行規則は関係ありません。ただし、短絡は、ツリーを評価する方法の正確な仕様です。


編集1:あなたの他のコメントの1つで、あなたは言った

あなたの算術例では、最後の加算の前に 2 つの乗算を評価する必要がありますが、それは優先順位を定義するものではありませんか?

はい、これが優先順位を定義するものです。これは確かにCに当てはまりますが、0 * 5 ^ 7頭の中で(C 以外の!) 式を評価する方法を考えてみてください。幅優先のボトムアップ ルールによると、結果を見つける前にandを評価する必要があります。しかし、わざわざ評価する必要はありません。あなたはただ「ええと、なぜなら、これは でなければならないから」と言って、右側のブランチ全体をスキップします. つまり、最終的な乗算を評価する前に、両側を完全に評価していません。短絡してしまいました。同様に、任意の5 ^ 7 = 57^*05 ^ 75 ^ 70 * x = 0x0false && _ == falsetrue || _ == true_、右辺に触れる必要はないかもしれません。これが、オペレータが短絡していることを意味します。C は乗算を短絡しませんが (言語はこれを行うことができます)、andを短絡&&||ます。

ショートサーキット0 * 5 ^ 7が通常の PEMDAS 優先順位規則を変更しないのと同様に、論理演算子をショートサーキットして&&も より高い優先順位を持つという事実は変わりません||。単なるショートカットです。最初に評価する演算子の側を選択する必要があるため、C は論理演算子の左側を最初に評価することを約束します。これが完了すると、特定の値について右辺を評価することを避ける明白な (そして便利な) 方法があり、C はこれを行うことを約束します。

あなたのルール (幅優先のボトムアップ式を評価する) も明確に定義されており、言語はこれを行うことを選択できます。ただし、便利な動作である短絡を許可しないという欠点があります。しかし、ツリー内のすべての式が明確に定義されており (ループがない)、純粋である (変数の変更や出力などがない) 場合、違いはわかりません。「および」および「または」の数学的定義がカバーしていないこれらの奇妙なケースでのみ、短絡が目に見えることさえあります。

また、左端の式を優先することによってショートサーキットが機能するという事実について、基本的なことは何もないことに注意してください。言語 Ɔ を定義することもできますが、whereとはを⅋⅋表しますが、とはループし、 とはそれぞれ false と true になります。これは、左辺ではなく右辺を最初に評価することを選択し、次に同じ方法で単純化することにちょうど対応しています。and\\||0 ⅋⅋ infiniteLoop()1 \\ infiniteLoop()infiniteLoop() ⅋⅋ 0infiniteLoop() \\ 1

簡単に言うと、優先順位が教えてくれるのは、解析ツリーを構築する方法です。構文解析木を評価するための唯一の賢明な順序は、明確に定義された純粋な値に対して、幅優先のボトムアップで評価するのように動作する順序です。未定義または不純な値の場合、何らかの線形順序を選択する必要があります。1 線形順序が選択されると、演算子の一方の側の特定の値によって、式全体の結果が一意に決定される場合があります (例:、、または)。このため、線形順序でその後に来るものの評価を完了せずに逃げることができる場合があります。Cは、論理演算子に対してこれを行うことを約束し、0 * _ == _ * 0 == 0false && _ == _ && false == falsetrue || _ == _ || true == true&&||それらを左から右の方法で評価し、それ以外の目的で評価することはありません。ただし、優先順位のおかげで、それがそうであり、そうでないことがtrue || true && falseわかります。truefalse

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

それ以外の

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

1:実際には、理論的には演算子の両側を並行して評価することもできますが、それは現時点では重要ではなく、C にとっては確かに意味がありません。これにより、より柔軟なセマンティクスが生じますが、side-効果(いつ起こる?)

于 2010-11-01T03:15:44.993 に答える
21

(true && true || false && false) は && が優先されて評価されます。

TRUE && TRUE = True

FALSE && FALSE = False

True || False = True

アップデート:

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

これが C++ で true を生成するのはなぜですか?

前と同じように、分解してみましょう。&& は || よりも優先順位が高い C++ ではブール ステートメントがショート サーキットします。

1 && 1 = True.

bool 値を整数値に変換すると、

false -> 0
true -> 1

式は、この (真) && (真) ステートメントを評価し、|| を短絡して、無限ループの実行を防ぎます。さらに多くのコンパイラ Juju が進行中であるため、これはこの例に適した状況の単純化されたビューです。

非短絡環境では、OR の両側が「評価」され、右側がハングするため、その式は永久にハングします。

優先順位について混乱している場合は、元の投稿で || の場合は次のように評価されます。&& より優先度が高かった:

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

右から左か左から右かは覚えていませんが、どちらの方法でも結果は同じです。2 番目の投稿で || 優先度が高かった:

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: 「ただし、|| は 2 番目の && の前に評価されるため、この例は && に優先順位がないことを証明しています。これは、&& と || の優先順位が等しく、左から右の順序で評価されることを示しています。」

「&& が優先されると、最初の && (1) が評価され、次に 2 番目の && (無限ループ) が評価され、プログラムがハングします。これは起こらないため、&& は || の前に評価されません。」

これらについて詳しく説明しましょう。

ここでは、2 つの異なることについて話しています。操作の順序を決定する優先順位と、プロセッサ サイクルを節約するためのコンパイラ/言語のトリックであるショート サーキットです。

最初に Precedence について説明しましょう。優先順位は「操作の順序」の省略形です。本質的に、次のステートメントが与えられた場合: 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

これまで私と一緒に?では、ショート サーキットについて説明します。C++ では、ブール ステートメントは「ショート サーキット」と呼ばれるものです。これは、コンパイラが特定のステートメントを調べて、評価のために「最適なパス」を選択することを意味します。次の例を見てください。

(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 と評価された場合、(false && false) が true であるかどうかを評価するためにクロック サイクルを費やす必要はありません。

C++ 常にショート サーキットですが、他の言語では「Eager」演算子と呼ばれるもののメカニズムが提供されています。

たとえば、プログラミング言語の Ada を考えてみましょう。Ada では、"AND" と "OR" は "Eager" 演算子です..すべてを強制的に評価します。

Ada では、(true AND true) OR (false AND false) は、OR を評価する前に (true AND true) と (false AND false) の両方を評価します。Ada はまた、AND THEN および OR ELSE で短絡する機能を提供します。これにより、C++ と同じ動作が得られます。

それがあなたの質問に完全に答えることを願っています。そうでない場合は、お知らせください:-)

更新 3: 最後の更新です。引き続き問題が発生する場合は、メールでお知らせします。

「|| 演算子の短絡が発生し、2 番目の && 式の実行が短絡された場合、それは || 演算子が 2 番目の && 演算子の前に実行されたことを意味します。これは、&& および || の左から右への実行を意味します ( &&優先ではありません)」

次に、この例を見てみましょう。

(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 は最初の 2 つでヒットするでしょう。また、3 番目の例でも 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 は操作のグループ化のみを設定します。この場合、&& は || より大きいです。

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())

それは一種の事実です..そして、私はこれを証明する方法を他に知りません. 優先順位は、言語の文法規則によって決定されます。コンパイラの最適化は、各コンパイラによって決定されます.いくつかは他のものよりも優れていますが最も少ない比較で最速の実行の「最良の」チャンスを与えるために、グループ化された比較を自由に並べ替えることができます。

于 2010-10-31T05:13:36.530 に答える
7

&&確かに優先順位が高いです

于 2010-10-31T05:08:23.097 に答える
7

2 つの事実が、両方の例の動作を説明しています。まず、 の優先順位 &&は よりも高くなって||います。次に、両方の論理演算子が短絡評価を使用します。

優先順位は評価の順序と混同されることがよくありますが、独立しています。最終結果が正しい限り、式の個々の要素を任意の順序で評価できます。一般に、一部の演算子では、演算子自体が適用される前に両方が評価される限り、左側の値 (LHS) が右側の値 (RHS) の前または後に評価される可能性があることを意味します。

論理演算子には特別な特性があります。特定のケースでは、片側が特定の値に評価される場合、反対側の値に関係なく、演算子の値が認識されます。このプロパティを便利にするために、C 言語 (および拡張によりすべての C ライク言語) は、論理演算子を指定して、RHS の前に LHS を評価し、さらに、RHSの結果を知るためにその値が必要な場合にのみ RHS を評価します。オペレーター。

したがって、 と の通常の定義を仮定するTRUEFALSETRUE && TRUE || FALSE && FALSEは左から評価されます。最初の式TRUEは最初の結果を強制しない&&ため、2 番目TRUEの式が評価され、次に式TRUE && TRUEが (TRUE に) 評価されます。これで、 は||その LHS を認識します。さらに良いことに、その LHS は の結果を||知ることを強制したので、RHS 全体の評価をスキップします。

2 番目のケースでは、まったく同じ評価順序が適用されます。の RHS ||は無関係なので、評価されず、呼び出しもinfiniteLoop()行われません。

この動作は仕様によるものであり、便利です。たとえばp && p->next、式が NULL ポインターを逆参照しようとしないことを知って書くことができます。

于 2010-11-01T02:23:13.487 に答える
3

"||演算子の短絡が発生し、2番目の&&式の実行が短絡した場合、それは||演算子が2番目の&&演算子の前に実行されたことを意味します。これは&&と||の左から右への実行を意味します( &&優先順位ではありません)。」

完全ではありません。

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

このように評価されます:

  1. 最初のオペレーターを確認してください。
  2. 評価xx && yy
  3. 次の演算子を確認してください。
  4. 次の演算子が||で、ステートメントの最初の部分がtrueの場合、残りをスキップします。
  5. それ以外の場合は、の次の演算子を確認し||、評価します。zz && qq
  6. 最後に、を評価し||ます。

私の理解では、C ++は、評価を開始する前に読み取りを行うように設計されています。結局のところ、この例では、が読み込まれるまで2番目の&&チェックがあることを認識していません。つまり、2番目に到達する前に||読み込む必要があります。したがって、最初の部分がtrueと評価された場合、それは後の部分を実行しませんが、最初の部分がfalseと評価された場合、最初の部分を実行し、を読み込んで、2番目の部分を見つけて評価します。 2番目の部分の結果を使用して、最終結果を決定します。||&&||||

于 2012-12-06T20:37:15.797 に答える
1

アンドリューの最新のコードに関しては、

#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。、

于 2010-11-01T04:19:50.173 に答える
1

and/or/true/false は */+/1/0 と非常に似ているため (数学的には同等です)、次のことも当てはまります。

1 * 1 + 0 * 0 == 1

むしろ覚えやすい...

そうです、それは優先順位短絡に関係しています。ブール演算の優先順位は、対応する整数演算にマップすればかなり簡単です。

于 2010-11-04T19:06:06.870 に答える
1

この例についてはtrue && true || infiniteLoop() && infiniteLoop()、2 つの特性が組み合わされているため、どちらの無限ループ呼び出しも評価されていません: && は || よりも優先され、|| は優先されます。左辺が真のとき短絡します。

&& と || の場合 同じ優先順位を持っていた場合、評価は次のようにする必要があります。

((( 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 )
于 2010-10-31T17:54:51.950 に答える
1

あなたの編集に関して: true || のため、infiniteLoop() は評価されません。(何でも) は常に true です。true | を使用 (なんでも) 何でも実行する必要がある場合。

于 2010-10-31T15:08:18.530 に答える
0

これは連続した図です:

  (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ます。

于 2010-11-01T03:00:49.583 に答える
0

簡単な答えは、&& が || よりも優先されるということです。さらに、ブール式の結果を知る必要がないため、コードは実行されません。はい、コンパイラの最適化です。

于 2010-11-01T03:41:40.250 に答える