Operandenauswertung

Hallo,

ich beschaeftige mich gerade mit dem ‚++‘-Operator und bin auf folgendes Problem gestossen:
Der ‚+‘ Operator wertet von links nach rechts aus, also mit ‚z++ + z‘ sollte zuerst ‚z‘ um eins erhoeht werden, die Rueckgabe ist der urspruengliche Wert von ‚z‘ und anschliessend mit dem neuen Wert von ‚z‘ addiert werden. Also muesste ich immer eine ungerade Zahl erhalten.
Nun habe ich aber folgendes Programm geschrieben:
#include
using namespace std;

int main()
{
int z = 4;

z = 4;
// Erwarte 5 + 5 = 10
cout

Hallo,

ich beschaeftige mich gerade mit dem ‚++‘-Operator und bin auf
folgendes Problem gestossen:
Der ‚+‘ Operator wertet von links nach rechts aus, also mit
‚z++ + z‘ sollte zuerst ‚z‘ um eins erhoeht werden, die
Rueckgabe ist der urspruengliche Wert von ‚z‘ und
anschliessend mit dem neuen Wert von ‚z‘ addiert werden. Also
muesste ich immer eine ungerade Zahl erhalten.
Nun habe ich aber folgendes Programm geschrieben:
#include

[…]

Kann mir jemand meinen Denkfehler erklaeren?

Der Denkfehler steckt im postfix increment:

z = 4;
cout

(Zusammenfassung: lustiges Problem mit Post-Increment)

Verlasse dich nie darauf, dass ein Ausdruck der ein In-/Dekrement enthält so auswertet wie du es erwartest. Die Ausführungsreihenfolge ist in C(++) ein ungelöstes - und dem Standard nach unlösbares - Problem.

hallo,
ich würde mal behaupten, der gesamte ausdruck wird vor bzw. nach dem inkrementieren ausgewertet und mit dieser voraussetzung entspricht das ergebnis genau den erwartungen.

z++ + z++ sollte doch wenn z 4 ist; 8 ergeben und z anschließend 6 sein, oder irre ich mich da?
ebenso ++z + ++z sollte 12 sein.

z++ + z++ sollte doch wenn z 4 ist; 8 ergeben und z
anschließend 6 sein, oder irre ich mich da?
ebenso ++z + ++z sollte 12 sein.

Würde man vermuten, tatsächlich ist das Verhalten für diesen Ausdruck nicht definiert. Der C++ Standard spricht von sogenannten „sequence points“, die Einfluß darauf nehmen, wann ein Object verändert werden kann/darf. Auf dem gcc 2.95.2 ist z=5, auf dem Sun CC ist z=6.
Beispielsweise wird ein sequence point gesetzt, sobald eine Funktion aufgerufen wird. Wäre z kein int sondern deine eigene Klasse mit entsprechend überladenen Operatoren, so sieht das Ergebnis aus, wie du es erwarten würdest.
Der operator-++ angewendet auf einen int ist keine echte Funktion, vielmehr verhält es sich so, als würde ein Funktion aufgerufen. Der C++ Compiler setzte dieses Ausdruck in Maschinensprache um, daher wirst du in der Standard-Bibliothek keine Funktionen für diesen Operator finden.

Ingesamt sollte man solche Ausdrücke vermeiden. So treten weniger Fehler auf und der Code ist für dritte besser lesbar. Spätestens wenn du dich in er Parameterliste einer Funktion befindest, gelten wieder andere Regeln. Hier stellt sich die Frage, werden die Parameter von link-nach-recht ausgewertet oder umgekehrt => compiler-spezifisch

Gruß Markus

Ich hatte das so verstanden, dass genau fuer solche Faelle die Prioritaet und die Assoziativitaet da ist. Nach der Prioritaet wird erst der +±Operator ausgewertet, anschliessend der ±Operator.
Und die Assoziativitaet hatt ich so verstanden, dass erst der linke Ausdruck (z.B. z++) ausgwertet wird, anschliessend der rechte und dann addiert wird.

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Ingesamt sollte man solche Ausdrücke vermeiden. So treten
weniger Fehler auf und der Code ist für dritte besser lesbar.
Spätestens wenn du dich in er Parameterliste einer Funktion
befindest, gelten wieder andere Regeln. Hier stellt sich die
Frage, werden die Parameter von link-nach-recht ausgewertet
oder umgekehrt => compiler-spezifisch

Ist dafuer nicht die Assoziativitaet da?
Mein urspruengliches Problem war, dass ich den bitweisen Unterschied zwischen ‚z‘ und ‚z + 1‘ berechnen und anschliessend ‚z‘ um eins erhoehen wollte (um alle moeglichen binaeren Zustaende fuer 20 0-1-Werte zu erhalten).
Gedacht hatte ich mir:
unsigned d = (z ^ ++z);
jetzt brauche ich dafuer zwei Zeilen:
unsigned d = (z ^ (z + 1));
++z;

Gruss
Diether

Ich hatte das so verstanden, dass genau fuer solche Faelle die
Prioritaet und die Assoziativitaet da ist. Nach der Prioritaet
wird erst der +±Operator ausgewertet, anschliessend der
±Operator.
Und die Assoziativitaet hatt ich so verstanden, dass erst der
linke Ausdruck (z.B. z++) ausgwertet wird, anschliessend der
rechte und dann addiert wird.

Auf die Ausführungsreihenfolge, also ob bei „z++ + z“ zuerst der linke oder rechte Operand ausgewertet wird, kann man sich meiner Erfahrung nach nie verlassen. I.A. wird es auch als ein wenig „böse“ angesehen, solche Konstrukte zu verwenden, da man dadurch außer Lesbarkeit absolut nichts spart.

Ist dafuer nicht die Assoziativitaet da?

Ich glaube, die Assoziativität wurde im Standard mit Absicht weggelassen, damit der Compiler besser optimieren kann, ohne daß man ihm die Generierung von falschem Code vorwerfen könnte.
Der Optimizer nutzt nämlich algebraische Eigenschaften wie Kommutativität aus !
Z.B. beim asm(…)-statement kann man angeben, daß es bezüglich bestimmter Operanden kommutativ ist - eben damit der Optimizer das ausnutzen kann. Wenn man z.B. MMX unterstützen will und es dafür keine builtins gäbe (bei neueren Compilern gibt’s das natürlich), könnte man sich dafür ein asm-statement für Vektor-Addition bauen und dabei noch angeben, daß es kommutativ ist. Das kann der Optimizer dann ausnutzen.
Soweit ich weiß, ist der ‚+‘-Operator auch als kommutativ notiert. Aber ich weiß nicht, warum der Compiler das in deinem Fall umstellen sollte. Aber es wäre vielleicht eine Erklärung.

Gruß
Thorsten