C++ was wird hier gecheckt?

...komplette Frage anzeigen

3 Antworten

Wer als Programmierer Code der Form  if ( i++ || j++ ) i++ ;  schreibt, der gehört fristlos entlassen.

Und dennoch gilt: Ja, es ist gültiger C++ Code. Bei seiner Abarbeitung hätte folgendes zu passieren (und wird auch passieren, falls man einen korrekt implementierten Compiler verwendet, worauf ich mich aber nicht verlassen würde):

Zuerst wird der boolsche Ausdruck ausgewertet und - als Nebeneffekt davon der Wert von i ebenso wie der Wert von j um 1 erhöht. War der Wert mindestens einer dieser beiden Variablen vorher 0, ergibt sich als Wert des boolschen Ausdrucks FALSE und nichts weiter passiert. Andernfalls ergibt sich TRUE und der Wert von i wird nochmals um 1 erhöht.

Wert des gesamten Ausdrucks ist der so entstandene neue Wert von i.

Warum man solchen Code nie schreiben sollte:

Compiler können die Auswertung boolscher Ausdrücke optimieren, indem sie argumentieren: Wenn das linke Argument i des Operators || Null, also FALSE, liefert, brauche ich das rechte Argument - in unserem Fall j - gar nicht mehr anzusehen. Korrekt ist das aber nur, wenn bei der Auswertung des rechten Arguments keine Seiteneffekte eintreten sollen. Darauf aber, dass die Hersteller des Compilers so weit gedacht haben, sollte man sich besser nicht verlassen.

Deswegen gehört fristlos entlassen, wer ohne Not Code schreibt, der nur tun wird, was beabsichtigt war, wenn auch die durch den Compiler vorgenommene Code-Optimierung fehlerfrei implemntiert ist.

Es ist wie Straßenverkehr auch: Es empfiehlt sich immer, ein bisschen Sicherheitsabstand zu anderen Fahrzeugen einzuhalten, damit kleine Fehler anderer uns nicht unverhältnismäßig großen Schaden bescheren können.

Ich hoffe, jeder hat gemerkt, dass ich in dieser Antwort gleich 2 Fehler gemacht habe:

  • Statt "War der Wert mindestens einer dieser beiden Variablen vorher 0, ergibt sich als Wert des boolschen Ausdrucks FALSE und nichts weiter passiert." muss es heißen: "Hatten beide Variablen vorher den Wert 0, passiert nach Hochzählen beider um jeweils 1 gar nichts".
  • Statt "Wenn das linke Argument i des Operators || Null, also FALSE, liefert, brauche ich das rechte Argument - in unserem Fall j - gar nicht mehr anzusehen" muss es heißen: "Wenn das linke Argument i des Operators || zuvor nicht Null, also TRUE, war, brauche ich das rechte Argument - in unserem Fall j - gar nicht mehr anzusehen.

Und natürlich sollte man statt

int i = 2, j = 3 ;  if (i++ || j++) i++;

besser einfach

int i = 4, j = 4 ;  

schreiben.

1

Wir einigen uns mal darauf, dass wer so etwas in einem produktiven Programm schreibt, geschlagen gehört ;).

Wichtig ist eben zu wissen, dass bei || und && der zweite Operant nicht beachtet werden, wenn das Ergebnis bereits feststeht.

Also:

a oder b. Ist a true, ist es völlig egal, was b ist. Somit wird b nicht angeschaut und true ausgegeben.

a und b. Ist a false, ist es völlig egal, was b ist. Somit wird b nicht angeschaut und false ausgegeben.

Mal durchgehen, wie das ausgeführt wird:

* Wird geprüft, ob i!=0 ist

* Anschließend wird i um eines hoch gezählt (wegen if(i_++_ || j++) i++;).

* Da i!=0 und somit true wird (i++ || j++) zu wahr ausgewertet OHNE j++ auszuführen. Einer der Gründe, wieso ich das ++-Zeugs in ifs hasse.

Ergebnis:

i=4; j=3

Wandeln wir das mal ab (i Startwet 0):

int i=0,j=3; if(i++ || j++) i++;

Jetzt ist j auf einmal 4. Denn i ist 0 (bevor es hochgezählt wurde). Somit false. Somit wird j++ ausgeführt.

Wandeln wir das noch einmal ab (i++ statt ++i):

int i=0,j=3; if(++i || j++) i++;

Jetzt ist j wieder 3. Da i nun zuerst um 1 hochgezählt wird. Dann 1 ist. Und somit false. Und somit j++ ausgeführt wird.

Deine Abwandlung (und statt oder):

int i=2,j=3; if(i++ && j++) i++;

Jetzt ist i und j gleich 4. i wird angeschaut. Ist true. Das heißt Ergebnis hängt davon ab, was j ist. Also wird j++ angeschaut - und somit j um eines nach oben gezählt.

Wäre z.B. i in diesem Fall 0, so würde j nicht hochgezählt werden. i wäre dann ja false - und somit würde das Ergebnis des (i++ && j++) bereits nach anschauen von i++ feststehen. Somit wäre i am Ende 1 und j 3.

Und so weiter.

Was du daraus lernen sollt: Niemals solchen unklaren Code schreiben!

Danke für die Antwort! Das hat mir auch einiges verständlicher gemacht. Diesen Code hab übrigends nicht ich geschrieben, sondern es ist ein beispiel aus einem Buch. :)

0

Zur Abwandlung von i++ zu ++i habe ich eine Frage. Die Seite https://de.wikibooks.org/wiki/C%2B%2B-Programmierung:_Operatoren#.2B.2B_.28Inkrement.29 verwirrt mich. Dort steht "Die Postfix-Notation (i++) hat eine höhere Priorität als die Präfix-Notation (++i)." Ich dachte wie du, dass ++i eine höhere Priorität als i++ hat.

Desweiteren sollte es doch lt. https://de.wikibooks.org/wiki/C%2B%2B-Programmierung:_Liste_der_Operatoren_nach_Priorit%C3%A4t keinen Unterschied machen, ob i++ oder ++i, weil beides eine höhere Priorität hat (2, bzw. 3) als || (14). Somit müsste in jedem Fall zuerst inkrementiert werden.

0
@Suboptimierer

Das sind die Stellen, wo es immer lustig wird ;).

* Wird geprüft, ob i!=0 ist

* Anschließend wird i um eines hoch gezählt (wegen if(i_++_ || j++) i++;).

Aus Nutzersicht kann man das so tatsächlich sagen. Mir ist aber auch gerade aufgefallen, dass das aus Programmsicht nicht stimmt.

Also: Egal ob ++i oder i++ - das i wird in beiden Fällen zur selben Zeit um eines hochgezählt.

Beides ist aber eigentlich ein Funktionsaufruf, der einen Rückgabewert hat.

Im Falle ++i wird das um eins erhöhte i zurückgegeben. Im Falle i++ wird das i zurückgegeben bevor es um 1 erhöht wurde.

Deshalb lese ich das auch meistens so, dass bei i++ das i eben erst hochgezählt wird NACHDEM die Zeile oder der Ausdrück ausgewertet worden ist. Auch wenn das nicht ganz der Implementierung entspricht ;).

Kannst die Zeilen aber auch mal eintippen - das unterschiedliche Ergebniss kommt tatsächlich heraus.

1
@Tuxgamer2

Beides ist aber eigentlich ein Funktionsaufruf, der einen Rückgabewert hat.

Gut erklärt. Denkt man sich es als Funktion mit den von dir genannten Rückgabewerten, wird es klar.
Seltsam ist, dass man ja meistens den erhöhten Wert zurückgegeben haben möchte, aber i++ in annähernd 100% aller Fälle verwendet wird. Steht es alleine, spielt es auch keine Rolle. Nur wenn i++ mit anderen Operationen vermischt wird, kann es einen Unterschied machen.

0

Das wirkt irgendwie ganz  schön getrickst und zurecht verwirrt dich dieser Ausdruck. Wenn jemand so später im Betrieb programmieren würde, würden er seine Kollegen und nach kurze Zeit sich selber schnell zur Verzweiflung bringen.

|| vergleicht Wahrheitswerte. Du gibst aber int-Werte als Parameter. Nun kommt es darauf an. Wenn 0 als false und alles andere als true gewertet wird, wird i++ erfolgreich geprüft und i++ erneut ausgeführt. j++ wird wahrscheinlich gar nicht mehr überprüft und somit j nicht inkrementiert.

Wenn es in Java so ist, dass || bitweise ints vergleicht, wird es komplizierter. Da würde ich mir dann eine Rangfolgetabelle neben den Computer legen.

Ich vermute für den Fall, dass i++=3 mit j++=4 bitweise oder verglichen werden würde, also 011 mit 100 und dann true enstehen und i++ ausgeführt, bei && würde false entstehen, sodass i++ nicht erneut ausgeführt werden würde.

Am besten ist es wohl, wenn du es einfach ausprobierst. ^^ Wenn das eine Hausaufgabe ist, empfehle ich wie gesagt, dir noch einmal die Rangfolgen der Operatoren anzuschauen.
_____________

Habe etwas dazu gefunden. Bitweise würde mit einfachem | und & verglichen werden.
https://stackoverflow.com/questions/4014535/differences-in-boolean-operators-vs-and-vs

Danke für die ausführliche Antwort! Diesen Code habe ich in dem Buch " C++ Professionell lernen und anwenden " gefunden und hat mich verwirrt. :)

0
@SABRINA2000s

PS: Meine Antwort ist aus dem Bauch heraus. Oft sind Kleinigkeiten entscheidend, wie ob (i++) || (j++) verglichen wird oder i || j und dann beides inkrementiert wird und ob die zweite Bedingung überhaupt geprüft wird, wenn die erste bereits als wahr interpretiert wird. Usf.

0

sorry, ich dachte vllt hat wer Ahnung der mit Java arbeiter...ich hätte noch ne frage. Du hadt gesagt er vergleicht 011 mit 001. Das sind beide true werte -> über 1. Das heißt das ergebnis ist true?

0
@SABRINA2000s

Das Ergebnis ist 111 = 7, welches als true interpretiert werden müsste. Bei & (bitweise und) kommt 000 = 0 heraus, was als false interpretiert werden müsste.

0

Ok danke für den link...jetzt ist mir es klarer geworden! aber z.b. 010 mit & währe 101. ist das nun true oder false?

0
@SABRINA2000s

101 & 010 = 000_bin = 0_dez

In den meisten Programmiersprachen ist 0 = false und alles andere true. Das kann, muss aber nicht so definiert werden. Was immer gleich ist, ist dass bitweise und 101 und 010 = 000 ergibt.

In Java kann man meine ich sogar noch den Datentyp mit auf Gleichheit überprüfen. Dafür verwendet man drei Gleichheitszeichen ===. Aber wir sind ja bei C++

0
@Suboptimierer

In Java kann man meine ich sogar noch den Datentyp mit auf Gleichheit
überprüfen. Dafür verwendet man drei Gleichheitszeichen ===.

Hey!

Java ist etwas völlig anderes als Javascript!!!

Java (und nebenbei auch C++) sind schöne typensichere Sprachen. Da kann man nicht 2 Sachen von inkompabiblen Typen prüfen - das wird statisch vom Compiler geprüft und im Zweifelsfall das Zeugs nicht compiliert.

Und ich frage mich immer noch, wer sich diesen Bullshit ausgedacht hat wie das == von Javascript.

Selbst bei anderen Skriptsprachen. Wenn der Datentyp sich unterscheidet, erwartest du klar "false". Und nicht, dass das Zeug irgendwie vermatscht wird und du nach irgendwelchen krass undurchsichtigen Regeln "true" oder "false" bekommst - dass du eigentlich eine Münze werfen kannst, um das Ergebnis vorherzusagen.

0
@Tuxgamer2

Ganz so ist es nicht. 

Die Aussage ist zwar korrekt, dass man nicht zwei Variablen mit inkompatiblen Typen vergleichen kann, aber int lässt mit bool vergleichen.

Zum Beispiel wird das hier erklärt: 

http://userpage.fu-berlin.de/~ram/pub/pub\_jf47ht81Ht/c++\_vergleichsoperator\_de

9 > 8 > 7 ist möglich.

In JavaScript ist 1 = true, alles andere ist false.

In C++ ist 0 = false, alles andere ist true.

https://stackoverflow.com/questions/5189072/c-bool-question

Gerade in Exceltipps nutze ich diese gängige Konvertierung sehr häufig, weil sie Schreibarbeit erspart.

Aber innerhalb von if-Bedingungen noch anfangen, zu inkrementieren usw. davon halte ich auch nichts. Im Zweifelsfall würde ich wenigstens Klammern setzen. Die haben eindeutig immer Priorität.

0
@Suboptimierer

Ja - C++ hat an dieser Stelle impliziete Typenconvertierung. Womit:
9 > 8 > 7
eine Abkürzung für:
((int)(9>8) ) > 7
ist - und du somit trotzdem nur ints vergleichst. Das Ergebnis ist übrigens bei dem Ausdruck oben false. Was den obrigen Ausdruck auch ziemlich sinnlos macht.

Viel interessanter ist aber doch aber, dass du in C++ auch den ==-Operator überschreiben kannst - und dann eigenes == für Typen implementieren kannst, die du sonst nicht vergleichen könntest. Sollte ich vielleicht nicht verschweigen...

In Java gibts dafür diesen "equals"-Hack - also dass jede Klasse einfach eine equals-Methode hat, die man bei Bedarf überschreiben kann.

>> Im Zweifelsfall würde ich wenigstens Klammern setzen.

>>Die haben eindeutig immer Priorität.

Das hier ist kein Prioritäten-Problem. Klammern ändern hier überhaupt nichts.

1
@Tuxgamer2

In PHP habe ich auch schon witzige Sachen gelesen.

Da kann man den else-Zweig im rechten Oder-Operanden unterbringen.

if(fileopen(...) or die()){...}

1

Ok Danke! Jetzt hab ich es verstanden :D

0

Was möchtest Du wissen?