Seite 1 von 1

Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 09:30
von Gelöschter Benutzer 2
Und zwar geht es um einen Tausch mit der Bitweisen XOR Operation in Java, konkret um das Beispiel im letzten Absatz innerhalb dieses Kapitels: http://openbook.galileocomputing.de/jav ... d1d26f8afb

Der Code sieht so aus:

Code: Alles auswählen

public class xor {
    public static void main(String[] args) {
        int x = 12, y = 49;
        y ^= x ^= y;
        x ^= y;
        System.out.println(x + ", " + y);

        x = 12;
        y = 49;
        x ^= y ^= x ^= y;
        System.out.println(x + ", " + y);
    }
}
Das erste funktioniert noch und es kommt "49, 12" raus, beim zweiten aber kommt genau wie im Buch erwähnt das hier raus: "0, 12", jedoch ist mir unklar wieso.

Ich bin nur so weit, dass es sich um eine merkwürdige Eigenheit von Java handeln muss, denn sowohl C++:

Code: Alles auswählen

#include <iostream>

using namespace std;

int main() {
    int x = 12, y = 49;
    x ^= y ^= x ^= y;
    cout << x << ", " << y;
}
als auch PHP:

Code: Alles auswählen

<?php
	$x = 12;
	$y = 49;
	$x ^= $y ^= $x ^= $y;
	echo $x . ", " . $y;
?>
liefern das richtige Ergebnis und zwar "49, 12".



Kennt zufällig jemand den Grund für das Verhalten in Java?

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 09:51
von Krishty
C++ verhält sich hier richtig, allerdings verhält sich Java auch wie gewollt …

In Java ist die Auswertungsreihenfolge eines Ausdrucks mit Wirkung (wie eben Zuweisung) strinkt links-nach-rechts; in C++ ist sie nicht spezifiziert. In x ^= y ^= x ^= y speichert Java für das erste x ^= den Wert, den x hat und benutzt diesen Wert statt dessen, den x hat, nachdem y ^= x ^= y ausgeführt wurde (und XOR mit sich selber ergibt immer Null). (Siehe auch §15.7.1, „Evaluate Left-Hand Operand First“.)

In C++ ist die Reihenfolge der Auswertung von Wirkungen vor einem Semikolon nicht spezifiziert; d.h. dein Compiler mag jetzt gerade Code erzeugen, der deinem Wunsch gerecht wird, anderen Compilern und auch demselben Compiler auf anderer Optimierungsstufe steht es jedoch frei, sich wie in Java zu verhalten. Du musst den C++-Code gleich dem Java-Code auf mehrere Zuweisungen aufspalten, wenn er portabel sein soll.

Wie es in PHP aussieht, weiß ich nicht; und ich bezweifle, dass es selbst von PHP-Programmierern viele wissen … darum solltest du dort auch splitten.

Gruß, Ky

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 10:16
von Gelöschter Benutzer 2
Mir geht es weniger darum wie man es im Endeffekt macht als viel mehr um das Verständnis wieso das passiert. Aber deine Erklärung ist einleuchtend. Im Buch wird das übrigens davor sogar erwähnt: http://openbook.galileocomputing.de/jav ... 054b4742bf (Erster Beispielkasten)
Krishty hat geschrieben:Wie es in PHP aussieht, weiß ich nicht; und ich bezweifle, dass es selbst von PHP-Programmierern viele wissen … darum solltest du dort auch splitten.
Wenn die überhaupt wissen was eine XOR Verknüpfung ist :P.

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 10:17
von Krishty
„Wieso das passiert“ im Sinne von, wieso sich bei der Spezifikation der einzelnen Sprachen dafür entschieden wurde?

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 10:19
von Gelöschter Benutzer 2
Das wäre auch interessant zu wissen wieso sie sich so entschieden haben, aber es war eher so gemeint aus welchem Grund das passiert. Ich dachte schon an irgendwelche Optimierungen die schief liefen...

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 10:25
von Krishty
Nein; zumindest bei C++ und Java entspricht es voll und ganz der Spezifikation, auch wenn sie bei C++ eben sagt, dass es unspezifiziert ist.

Man muss sich bei der Sprachspezifikation eben dafür entscheiden, in welcher Reihe die Wirkungen eintreten sollen, sonst werden solche Programme unberechenbar (wie es bei C und C++ der Fall ist und wohl wegen der ewigen Abwärtskompatibilität nicht mehr geändert werden wird). Warum man sich bei Java (und auch C#) ausgerechnet für links-nach-rechts entschieden hat (statt einer Auswertung in Reihenfolge der Operatoren, die würde deinen C++-Code dann auch wie gewollt umsetzen), weiß ich nicht. Vielleicht kann uns da einer der Profis auf die Sprünge helfen, es interessiert mich nämlich auch brennend und ich habe nirgends gute Argumente gefunden.

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 11:24
von Schrompf
Bei C++ ist es nach meinem Wissen übrigens spezifiziert. Siehe http://www.cppreference.com/wiki/operator_precedence - alle ZuweisungPlusOperation-Operatoren werden von rechts nach links aufgelöst.

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 11:32
von Gelöschter Benutzer 2
Genauso wie in Java da werden solche Operatoren auch von Rechts nach Links abgearbeitet. Aber die Operanden werden von Links nach Rechts abgearbeitet und nicht in der Reihenfolge der Operatoren wie bei mir in C++.

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 11:33
von Krishty
Falsch! Die Operatoren abzuarbeiten ist etwas völlig anderes als die Operanden auszuwerten!

Bei C++ muss die Auswertung nur vor Semikolon, Komma und logischen Operatoren erfolgen. In welcher Reihenfolge, sagt der Standard nicht. Ihr geht davon aus, dass x zweimal ausgewertet wird, nämlich vor und nach der Zuweisung – der Standard sagt aber, dass es nur einmal ausgewertet werden muss, und ob vor oder nach der Zuweisung ist der Implementierung überlassen. Ebenso sagt Java, dass es nur einmal ausgewertet wird, und zwar vor der Zuweisung, da es links davon steht.

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 11:36
von Schrompf
Ah. Oh. Ok, mein Fehler.

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 12:41
von Aramis
Krishty hat es richtig erklaert, mit einem Fehler ( :-) ).
In C++ ist die Reihenfolge der Auswertung von Wirkungen vor einem Semikolon nicht spezifiziert
Es ist *nicht* unspecified. Es ist undefined.

unspecified hieße: der Compiler muss dokumentieren wie seine Evaluierungsreihenfolge bei bestimmten Settings aussieht, und er muss konsistent bleiben. undefined heisst, er darf sich bei jeder Gelegenheit neu entscheiden.

Uebrigens hat es relativ wenig mit dem Optimizer und dem tatsaechlich generierten Maschinencode zu tun: dieser darf sowieso optimieren wie er will, sofern sich das mit Sprachmitteln beobachtbare Programmverhalten nicht aendert ('as is'-Prinzip).

Der Standard (C++0x - 1.9, 14-15)
14 Every value computation and side effect associated with a full-expression is sequenced before every value
computation and side effect associated with the next full-expression to be evaluated.

15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual
expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution
of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be
performed consistently in different evaluations. —end note ] The value computations of the operands of an
operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar
object is unsequenced relative to either another side effect on the same scalar object or a value computation
using the value of the same scalar object, the behavior is undefined.
Meine Theorie zu Java's und C#'s Verhalten: es gibt enorm viele verschiedene Moeglichkeiten eine eindeutige und nicht voellig sinnlose Regel fuer die Abarbeitungsreihenfolge aufzustellen. Alle Operanden vorher zu evaluieren, in genau definierter Reihenfolge, ist schlichtweg eine der einfachsten Varianten.

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 13:04
von Krishty
Ups, ja … fieser Terminus und ich habe hier keine Bibel parat, thx.

Übrigens ist auch die Reihenfolge der Auswertung von Funktionsparameter unspecified (?) (, also bloß nie foo(x = 3, ++x); schreiben) … hier ist das aber wohl auf die Calling-Conventions zurückzuführen, schätze ich. Jedenfalls muss es auch eine ähnliche Ursache für die Auswertung der Operanden geben …

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 13:42
von Aramis
Auch das ist laut Standard undefined. Genauer: der Standard verweist auf die gleiche Stelle. Wenn ich es richtig deute, sieht es sogar noch schlimmer aus:

Code: Alles auswählen

void foo() {};
typedef void (*)() fooptr;

fooptr d() {
   return foo;
}

d()(a,b,c)
… hier ist die Ausfuehrungsreihenfolge von a,b,c,d undefiniert. Sicher ist nur dass foo() erst aufgerufen wird wenn alle 4 evaluiert wurden.

Re: Merkwürdiges Verhalten bei mehreren ^= Operatoren

Verfasst: 13.09.2010, 13:50
von Krishty
Ich wollte undefined schreiben und habe mich dann davon ablenken lassen, dass unspecified in Courier New geschrieben war! Verdammt! Ich hasse es, wenn Leute wissen, wie sie meine Gedanken manipulieren können … werde mir schnell einen Helm aus Alufolie basteln, der sollte helfen …