なぜこのifステートメントと代入および等式チェックの組み合わせがtrueを返すのですか?


216

私はいくつかの初心者の間違いを考えていました、そして私はif声明にあるもので終わりました。私はコードをこれに少し拡張しました:

int i = 0;
if (i = 1 && i == 0) {
    std::cout << i;
}

私はそれが見てきたif文はtrueを返し、それはcout「S iのように1。場合はi割り当てられている1if文で、なぜやりましたi == 0戻りますかtrue


83
皆さん、これはタイプミスの質問ではありません。OPは、iがに設定されているため、ifコードにこのコードを入力した理由を知りたいと考えてい1ます。
NathanOliver

24
またはそれはの結果を割り当て1 && i == 0ますか?
JVApen

4
初心者のための提案:彼らはそのような「高度な」言語構成を使用すべきではありません。変数を個別に割り当てるだけです。これにより、シーケンスポイントで発生する可能性のある問題も回避されます。実際のコードでこの種のコードを使用すると、通常は見栄えも悪くなります。
user202729

1
これは最終的にインタビューの質問になる
でしょう

回答:


391

これは、演算子の優先順位に関係しています。

if (i = 1 && i == 0)

ではありません

if ((i = 1) && (i == 0))

両方&&==優先順位がよりも高いです=。それが本当にうまくいくのは

if (i = (1 && (i == 0)))

その結果割り当て1 && (i == 0)にしますi。あれば、iから始まり0、その後はi == 0あるtrue、そう1 && trueであるtrue(または1)、その後iに設定されます1。次に、1trueなので、ifブロックを入力して、割り当てた値を出力しますi


2
@JörgWMittagそれはかなりクールです。括弧を使わざるを得ないのが好きです。
NathanOliver

基本的に、i = !i; if (i)適切に書かれています
カカウエテフリト

@NathanOliver:Fortressは、多くのことを正しく理解する、かなりクールな言語でした。(主な設計者はGuy L. Steeleだったので、そこに驚きはありません。)残念ながら、DARPA資金の最終ラウンドには採用されず、その後Oracleに押し寄せました。
イェルクWミッターク

6
当然のことながら、コンパイラから警告を少しでも要求すると、そのエラーが検出されます
Deduplicator

4
intとbooleansが同等であると想定していない言語もこれを取り上げます。
rghome

16

コードが実際に次のようになっていると仮定します。

#include <iostream>
using namespace std;

int main()  {
    int i = 0;
    if (i = 1 && i == 0) {
        cout << i;
    }
}

次にこれ:

if (i = 1 && i == 0) {

として評価

 if (i = (1 && i == 0)) {

i設定されてい1ます。


39
追加のコードは本当に必要でしたか?他の方法では実行されないので、これが当てはまることは明らかです。
Modelmat

13
不要な余分なコードだけではありません。答えは、演算子の優先順位を明確に説明していません。
フランシスコサラボゾ

34
私たちはナイトピック列車に乗っているので...私はusing namespace std
Mateen Ulhaq

8
余分なコードがありますが、それでもまだ正しくないコードではありません。そして、答えはまだ正しいです。もちろん、演算子の優先順位については説明していません。しかし、全面的な反対投票の代わりに、誰かが追加することを提案する可能性があります!
B Charles H

14
うわー、-4は厳しいです。これが質問に正しく答えることを考えると、最適ではないかもしれません。演算子の優先順位は他の回答ほど拡張されていませんが、コードのコンテキストではそれについて十分に述べているので、=前に来たと思った人は誰でも&&問題を見ることができます。また、そうです、拡張は無関係ですが、それほど重要ではないと思います。私はそのような小さな違いが人々に151から-4を投票させるとは信じられません。
-JoL、

-4

これは、右から左へのルールの解析に関係しています。たとえば、y = x + 5。
すべての部分式は重要度に重み付けされています。重要度が等しい2つの式は、右から左に評価されます。&&式の側が最初に行われ、次にLHSが続きます。

私には理にかなっています。


1
関連性(「右から左への規則」)はこれとは関係ありません。これは優先順位(「重要度」)に関するものであり、使用される演算子の優先順位は同じではありません
、オービットの軽量レース

-4

実際の答えは:

  1. コンパイラーは"i == 0" を優先し、trueと評価されます。
  2. 次に、i = 1をTRUEまたはFALSEとして評価します。コンパイルされた代入演算子は失敗しない(そうでなければコンパイルされない)ので、trueと評価されます。
  3. どちらのステートメントもtrueと評価され、TRUE && TRUEはTRUEと評価されるため、ifステートメントはTRUEと評価されます。

証明として、入力したコードのコンパイラのasm出力を確認してください(すべてのコメントは自分のものです)。

mov     dword ptr [rbp - 8], 0    ; i = 0;
cmp     dword ptr [rbp - 8], 0    ; i == 0?
sete    al                        ; TRUE (=1)
mov     cl, al
and     cl, 1                     ; = operator always TRUE
movzx   edx, cl
mov     dword ptr [rbp - 8], edx  ; set i=TRUE;
test    al, 1                     ; al never changed,
                                  ; so final ans is TRUE

上記のasm出力はCLANGからのものでしたが、私が調べた他のすべてのコンパイラーも同様の出力を出しました。これは、サイトのすべてのコンパイラーに当てはまります。それらが純粋なCコンパイラーであるか、C ++コンパイラーであるかに関係なく、コンパイラーのモードを変更するプラグマはありません(デフォルトではC ++コンパイラーのC ++です)。

コンパイラは実際にはi = 1ではなく、i = TRUEを設定したことに注意してください(ゼロ以外の32ビットの整数値を意味します)。これは、&&演算子はステートメントがTRUEかFALSEかを評価するだけであり、その結果に従って結果を設定するためです。証明として、i = 1をi = 2に変更してみてください。何も変更されないことが自分で確認できます。Compiler Explorerでオンラインコンパイラを使用して自分の目で確かめてください


1
1)この質問がC ++でタグ付けされている場合、ドキュメントはC演算子の優先順位にリンクします。2つの異なる言語。2a)i = 1代入演算子[同等ではない]です。2b)if (i = 0)CとC ++の両方でfalse条件に評価されることを保証できます。したがって、「決して失敗しない」という条件でtrueと評価されるかどうかは、多少誤解を招きます。
TrebledJ

1
and cl, 1 ; = operator always TRUE<<私が間違っている場合は修正しますが、ここには割り当てがありません。1 &&式の一部を表します。したがって、この答えは基本的にに評価されfalseます。
syck

「この質問がC ++でタグ付けされている場合、ドキュメントはC演算子の優先順位にリンクします。2つの異なる言語」-CとC ++演算子の優先順位を比較すると、2つの違いは何ですか?このトピックに関しては、C ++がCの直接的な派生物である(または、CがC ++言語のサブセットであるため、C ++言語のサブセットであるため、当然のことながら、それらは同じ優先順位を持っています)優先順位を含む多くの共通点)。混乱を招く場合に備えて、とにかく投稿を修正します。
ar18

「私が間違っている場合は修正してください。ただし、ここには割り当てがありません」-次に、修正させてください!1は即値であり、テストや計算の結果ではありません。これは、いわゆる「推定TRUE」値です。行われる唯一のテストは、i == 0ステートメント、つまり-"cmp dword ptr [rbp-8]、0"のテストです。「movzx edx、1」と表示されている場合にのみ正しいでしょう。私の前のすべての投稿によると、2つの比較があるはずですが、実際には1つしかありません。すべての主要なコンパイラのasm出力は、それらの投稿が完全に正しくないことを証明します。
ar18

2
優先順位を間違えるだけでなく(正しい解析についてはNathanOliverの回答を参照)、代入演算子は常にTRUEに評価されるという誤った主張をします。お試しくださいif ( i = 0 ) { print something }。また、あなたの答えはそれ自体と矛盾しています。最初に、i=1前に評価されたこと&&が適用されると言い、最後に、それi&&演算子の結果に設定されると言います。
MM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.