Warum gibt diese if-Anweisung, die Zuweisung und Gleichheitsprüfung kombiniert, true zurück?

216

Ich habe an einige Anfängerfehler gedacht und bin zu dem auf der ifAussage gekommen. Ich habe den Code ein bisschen erweitert:

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

Ich habe gesehen , dass die ifAnweisung gibt wahr, und es coutist iso 1. Wenn in der if-Anweisung izugewiesen 1, warum wurde i == 0zurückgegeben true?

TehMattGR
quelle
83
Leute, das ist keine Tippfehlerfrage. Das OP möchte wissen, warum die if-Anweisung mit diesem Code eingegeben wird, da auf gesetzt iist 1.
NathanOliver
24
Oder ordnet es das Ergebnis von zu 1 && i == 0?
JVApen
4
Vorschlag für Anfänger: Sie sollten kein solches "fortgeschrittenes" Sprachkonstrukt verwenden. Weisen Sie die Variable einfach separat zu. Dadurch werden auch mögliche Probleme mit dem Sequenzpunkt vermieden. Diese Art von Code im praktischen Code sieht normalerweise auch schlecht aus.
user202729
1
Dies wird zwangsläufig zu einer Interviewfrage führen
RAZ_Muh_Taz

Antworten:

391

Dies hat mit der Priorität des Operators zu tun .

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

ist nicht

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

weil beide &&und ==eine höhere Priorität haben als =. Was es wirklich klappt ist

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

die ordnet das Ergebnis 1 && (i == 0)zu i. Also, wenn ibeginnt bei 0dann i == 0ist true, so 1 && trueist true(oder 1), und dann iauf wird eingestellt 1. Da dies 1wahr ist, geben Sie den if-Block ein und drucken den Wert, den Sie zugewiesen haben i.

NathanOliver
quelle
2
@ JörgWMittag Das ist ziemlich cool. Ich mag es, dass es dich zwingt, Klammern zu verwenden.
NathanOliver
Grundsätzlich i = !i; if (i)richtig geschrieben
Cacahuete Frito
@ NathanOliver: Fortress war eine ziemlich coole Sprache, die viele Dinge richtig gemacht hat. (Der Hauptdesigner war Guy L. Steele, also keine Überraschung.) Leider wurde es nicht für die letzte Runde der DARPA-Finanzierung abgeholt und anschließend von Oracle eingemottet.
Jörg W Mittag
6
Wenn Sie vom Compiler ein Minimum an Warnungen angefordert hätten, wäre dieser Fehler natürlich aufgetreten.
Deduplicator
4
Jede Sprache, die nicht davon ausgeht, dass Ints und Boolesche Werte gleichwertig sind, würde dies ebenfalls aufgreifen.
rghome
16

Angenommen, Ihr Code sieht tatsächlich so aus:

#include <iostream>
using namespace std;

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

Dann das:

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

bewertet als

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

und so iist eingestellt auf1 .


quelle
39
War der zusätzliche Code wirklich notwendig? Es scheint ziemlich offensichtlich, dass dies der Fall wäre, da es sonst nicht laufen würde.
Modelmat
13
Nicht nur unnötiger zusätzlicher Code. Die Antwort erklärt den Vorrang des Bedieners nicht klar.
Francisco Zarabozo
34
Da wir im Nitpick-Zug sind ... sehe ich ein using namespace std!
Mateen Ulhaq
8
Es gibt zusätzlichen Code - aber es ist immer noch kein falscher Code. Und die Antwort ist immer noch richtig. Natürlich erklärt es nicht die Priorität des Operators. Aber jemand könnte vorschlagen, dass es hinzugefügt wird, anstatt direkt abzustimmen!
B Charles H
14
Wow, -4 ist hart, wenn man bedenkt, dass dies die Frage richtig beantwortet, wenn auch vielleicht nicht optimal. Es erweitert die Priorität des Operators nicht so sehr wie die andere Antwort, aber es sagt gerade genug darüber im Kontext des Codes aus, so dass jeder, der dachte, dass er =vorher gekommen &&ist, das Problem sehen kann. Auch ja, die Erweiterung ist irrelevant, aber ich denke nicht, dass es so wichtig ist. Ich kann nicht glauben, dass solche kleinen Unterschiede dazu führen, dass die Leute zwischen 151 und -4 stimmen.
JoL
-4

Es hat mit dem Parsen der Regeln von rechts nach links zu tun. ZB y = x + 5.
Alle Unterausdrücke werden nach Wichtigkeit gewichtet. Zwei gleich wichtige Ausdrücke werden von rechts nach links ausgewertet. Die && Ausdrucksseite wird zuerst ausgeführt, gefolgt von der LHS.

Für mich ergibt das Sinn.

Leslie Satenstein
quelle
1
Assoziativität ("Regeln von rechts nach links") hat damit nichts zu tun. Es geht um Vorrang ("Wichtigkeit"), und die verwendeten Operatoren haben nicht den gleichen Vorrang.
Leichtigkeitsrennen im Orbit
-4

Die eigentliche Antwort lautet:

  1. Der Compiler gibt "i == 0" Vorrang , was als wahr ausgewertet wird.
  2. Dann wird i = 1 als TRUE oder FALSE ausgewertet, und da kompilierte Zuweisungsoperatoren niemals fehlschlagen (andernfalls würden sie nicht kompilieren), wird es auch als true ausgewertet.
  3. Da beide Anweisungen als wahr ausgewertet werden und TRUE && TRUE als TRUE ausgewertet wird, wird die if-Anweisung als TRUE ausgewertet.

Schauen Sie sich zum Beweis einfach die asm-Ausgabe Ihres Compilers für den von Ihnen eingegebenen Code an (alle Kommentare sind meine eigenen):

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

Die obige ASM-Ausgabe stammte von CLANG, aber alle anderen Compiler, die ich mir ansah, gaben eine ähnliche Ausgabe. Dies gilt für alle Compiler auf dieser Site, unabhängig davon, ob es sich um reine C- oder C ++ - Compiler handelt. Alle haben keine Pragmas, um den Modus des Compilers zu ändern (der für die C ++ - Compiler standardmäßig C ++ ist).

Beachten Sie, dass Ihr Compiler nicht i = 1 gesetzt hat, sondern i = TRUE (was bedeutet, dass jeder 32-Bit-Ganzzahlwert nicht Null ist). Dies liegt daran, dass der Operator && nur auswertet, ob eine Anweisung TRUE oder FALSE ist, und dann die Ergebnisse entsprechend diesem Ergebnis festlegt. Versuchen Sie als Beweis, i = 1 in i = 2 zu ändern, und Sie können selbst beobachten, dass sich nichts ändern wird. Überzeugen Sie sich selbst von einem beliebigen Online-Compiler im Compiler Explorer

ar18
quelle
1
1) Die Dokumente sind mit der Priorität des C-Operators verknüpft, wenn diese Frage mit C ++ gekennzeichnet ist. Zwei verschiedene Sprachen. 2a) i = 1ist ein Zuweisungsoperator [keine Äquivalenz]; 2b) Ich kann Ihnen versichern, dass if (i = 0)sowohl in C als auch in C ++ ein falscher Zustand vorliegt. Es ist also etwas irreführend, ob es als wahr bewertet wird, "es scheitert nie".
TrebledJ
1
and cl, 1 ; = operator always TRUE<< korrigiere mich, wenn ich falsch liege, aber ich sehe hier keine Zuordnung. Es repräsentiert den 1 &&Teil des Ausdrucks. Diese Antwort ergibt sich also grundsätzlich zu false.
Syck
"Die Dokumente sind mit der Priorität des C-Operators verknüpft, wenn diese Frage mit C ++ gekennzeichnet ist. Zwei verschiedene Sprachen" - und wenn Sie die Priorität des C-Operators mit dem C ++ - Operator vergleichen, was ist der Unterschied zwischen den beiden? Sie haben in Bezug auf dieses Thema den gleichen Vorrang, was nicht überraschend ist, da C ++ eine direkte Ableitung von C ist (oder anders ausgedrückt: C ist eine Teilmenge der C ++ - Sprache, also werden sie es natürlich haben viel gemeinsam, einschließlich Vorrang). Ich werde meinen Beitrag trotzdem reparieren, falls das verwirrend ist.
18.
"Korrigieren Sie mich, wenn ich falsch liege, aber ich sehe hier keine Zuordnung" - Dann lassen Sie sich von mir korrigieren! Die 1 ist ein unmittelbarer Wert und nicht das Ergebnis eines Tests oder einer Berechnung. Dies wird als "vermuteter WAHRER" Wert bezeichnet. Der einzige Test, der stattfindet, ist für die Anweisung i == 0, dh - "cmp dword ptr [rbp - 8], 0". Sie wären nur richtig, wenn es "movzx edx, 1" gesagt hätte. Laut ALLEN Posts vor mir sollte es zwei Vergleiche geben, aber im wirklichen Leben gibt es nur einen, und die ASM-Ausgabe von JEDEM großen Compiler beweist, dass diese Posts völlig falsch sind.
18.
2
Sie haben nicht nur die falsche Priorität (siehe NathanOlivers Antwort für die korrekte Analyse), sondern auch die falsche Behauptung, dass der Zuweisungsoperator immer TRUE bewertet. Versuchen Sie es if ( i = 0 ) { print something }. Auch Ihre Antwort widerspricht sich; Am Anfang sagen Sie, dass i=1das ausgewertet wird, bevor &&es angewendet wird, und am Ende sagen Sie, dass ies auf das Ergebnis des &&Operators gesetzt ist.
MM