Hallo,
vielleicht OT, weil nicht Linux, aber es ist C++ und passiert auch unter Linux ;-)
Hier folgen zwei kleine Programme und ich suche eine plausible Erklärung für die Ausgabe. (Passieren hier undefinierte Dinge? Wenn ja, wo steht, daß es undefiniert ist, was passiert?)
Probiert sowohl mit gcc version 2.95.2 20000220 (Debian GNU/Linux) als auch mit MSCVC++ 6.0. (Wenn ich mich recht erinnere, dann dort mit den selben Resultaten.)
Danke schonmal.
Heiko
#include <iostream> using namespace std;
int main() { int z = 5;
// (1) // was passiert? Werden wir 67 oder 76 sehen? cout << ++z << ++z << endl; // -> 76
double d = 5; int i = 5;
// (2) // Und was werden wir hier wohl sehen? // (Was überhaupt, und auch zweimal das selbe?) cout << ++d * ++d << endl; // -> 42 cout << ++i * ++i << endl; // -> 49
return 0; }
Vielleicht kann das ja einer, der sich als C++-Experte fühlt, mal bitte ausprobieren. Für C-Jünger ist wenigstens noch ein Problem (das erste):
#include <stdio.h>
int main() { int z = 5; double d = 5; int i = 5;
// was passiert? Werden wir 67 oder 76 sehen? printf("%d%d\n", ++z, ++z);
// -> Es ist jedesmal 42 zu sehen, das leuchtet mir auch // einigermaßen ein. Ist also in C kein Problem. printf("%f\n", ++d * ++d); printf("%f\n", ++i * ++i);
return 0; }
Hallo,
On Sunday 15 July 2001 14:40, Heiko Schlittermann wrote:
vielleicht OT, weil nicht Linux, aber es ist C++ und passiert auch unter Linux ;-)
Also dann, mal wieder den Stroustrup aus dem Regal gekramt....
Hier folgen zwei kleine Programme und ich suche eine plausible Erklärung für die Ausgabe. (Passieren hier undefinierte Dinge? Wenn ja, wo steht, daß es undefiniert ist, was passiert?)
Lt. Stroustrup "Die C++ Programmiersprache", 2. Auflage (dt.), Seite 102, 3.2.2 Auswertungsreihenfolge:
"Die Reihenfolge, in der Unterausdrücke in einem Ausdruck ausgewertet werden, ist undefiniert."
Probiert sowohl mit gcc version 2.95.2 20000220 (Debian GNU/Linux) als auch mit MSCVC++ 6.0. (Wenn ich mich recht erinnere, dann dort mit den selben Resultaten.)
[cut]
Die Resultate für z sind also völlig in Ordnung. Die beiden Compiler haben sich halt entschieden die Sub-Ausdrücke von rechts nach links auszuwerten (siehe Anhang).
Das Resultat für d ist auch ok: d wird hochgezählt, das Ergebnis auf den Stack gelegt und dann multipliziert.
Um das Ergebnis von i zu erklären muss man etwas weiter Richtung Assembler gehen (insbesondere, wenn man den Zusammenhang mit z verstehen will):
Nehmen wir mal an int ist eine Klasse:
class int { public: int& operator++();
int operator*(int&); };
Im Fall z existiert dieser Ausgabeoperator:
ostream& operator<<(ostream&,int);
also wird auf den increment-Operator zurückgegriffen und eine Kopie auf den Stack gelegt, da der Ausgabeoperator nur mit Kopien umgehen kann. Die beiden Kopien enthalten die Werte 6 und 7. In welcher Reihenfolge bleibt dem Compiler überlassen.
Im Fall i existiert ein Multiplikationsoperator, der mit Referenzen umgehen kann. Also wird i zweimal erhöht, es wird aber immer nur eine Referenz gespeichert wird, die dann an dem *-Operator übergeben wird. Ergebnis: der *-Operator löst beide Referenzen auf und bekommt beide male den Wert 7, was zum Ergebnis 49 führt.
Sehr unsauber, äußerst unerwartet aber durchaus im Rahmen der recht lockeren Spezifikationen.
Vielleicht kann das ja einer, der sich als C++-Experte fühlt, mal bitte ausprobieren. Für C-Jünger ist wenigstens noch ein Problem (das erste):
Auch bei C ist vollkommen undefiniert in welcher Reihenfolge Sub-Ausdrücke ausgewertet werden.
Es gibt nur einen Fall in dem es streng definiert ist (zumindest bei C++): die Operatoren && und ||. Deren Ausdrücke _müssen_ von links nach rechts ausgewertet werden. (Was aber einige Compilerbauer nicht davon abhält es trotzdem anders zu machen.)
Konrad
On Sun, Jul 15, 2001 at 02:40:26PM +0200, Heiko Schlittermann wrote:
// was passiert? Werden wir 67 oder 76 sehen? printf("%d%d\n", ++z, ++z); // -> Es ist jedesmal 42 zu sehen, das leuchtet mir auch // einigermaßen ein. Ist also in C kein Problem. printf("%f\n", ++d * ++d); printf("%f\n", ++i * ++i);
Lies mal die C-FAQ (http://www.faqs.org/faqs/C-faq/faq/) Abschnitt 3 insbesondere 3.2.
Reinhard
On Sun, Jul 15, 2001 at 05:23:15PM +0200, Reinhard Foerster wrote:
On Sun, Jul 15, 2001 at 02:40:26PM +0200, Heiko Schlittermann wrote:
printf("%f\n", ++d * ++d); printf("%f\n", ++i * ++i);
Lies mal die C-FAQ (http://www.faqs.org/faqs/C-faq/faq/) Abschnitt 3 insbesondere 3.2.
Gott ist mir schlecht. Manchmal verstehe ich Lutz Donnerhacke...
Gruß, Eric
On Sun, Jul 15, 2001 at 11:55:35PM +0200, Eric Schaefer wrote:
On Sun, Jul 15, 2001 at 05:23:15PM +0200, Reinhard Foerster wrote:
On Sun, Jul 15, 2001 at 02:40:26PM +0200, Heiko Schlittermann wrote:
printf("%f\n", ++d * ++d); printf("%f\n", ++i * ++i);
Lies mal die C-FAQ (http://www.faqs.org/faqs/C-faq/faq/) Abschnitt 3 insbesondere 3.2.
Gott ist mir schlecht.
Wieso? Der Kram ist undefiniert. Da man üblicherweise deterministische Programme schreiben will, macht man sowas nicht. Wo liegt das Problem?
Interessant an der Sache ist höchstens, daß nicht nur die Ausgabe unklar ist, sondern auch die Werte von d und i am Ende. Es spricht nichts dagegen, dass beide am Ende 0 sind wenn ich die FAQ richtig überflogen habe. (siehe 3.9)
BTW: Die Version des C99-Standards, die ich hier habe, ist 566 Seiten lang und eignet sich eher zum Nachschlagen als zum Durcharbeiten :)
Reinhard
On Mon, Jul 16, 2001 at 12:33:48AM +0200, Reinhard Foerster wrote:
Lies mal die C-FAQ (http://www.faqs.org/faqs/C-faq/faq/) Abschnitt 3 insbesondere 3.2.
Gott ist mir schlecht.
Wieso? Der Kram ist undefiniert. Da man üblicherweise deterministische Programme schreiben will, macht man sowas nicht. Wo liegt das Problem?
Darin, daß es undefiniert ist. Wenn ein Standard besagt, daß der Inhalt einer uninitialisierten Variable undefiniert ist, dann ist das OK, aber sowas hat einfach mal definiert zu sein. Und wenn es schon nicht so ist, dann sollte es riesengroß am Anfang eines jeden Lehrbuches stehen. Bäkse.
Interessant an der Sache ist höchstens, daß nicht nur die Ausgabe unklar ist, sondern auch die Werte von d und i am Ende. Es spricht nichts dagegen, dass beide am Ende 0 sind wenn ich die FAQ richtig überflogen habe. (siehe 3.9)
Eben. Solche Ausdrück, wie Heiko sie angegeben hat, sind recht üblich... Ich winde das einfach nur ekelhaft. Wie kann man in einer Sprache einen Operator definieren, der derart undefiniertes Verhalten erzeugen kann. Nochmal Bäkse.
Gruß, Eric
On Monday 16 July 2001 00:33, Reinhard Foerster wrote:
BTW: Die Version des C99-Standards, die ich hier habe, ist 566 Seiten lang und eignet sich eher zum Nachschlagen als zum Durcharbeiten :)
Wo bekommt man den eigentlich her?
Konrad
On Mon, Jul 16, 2001 at 08:59:00PM +0200, Konrad Rosenbaum wrote:
On Monday 16 July 2001 00:33, Reinhard Foerster wrote:
BTW: Die Version des C99-Standards, die ich hier habe, ist 566 Seiten lang und eignet sich eher zum Nachschlagen als zum Durcharbeiten :)
Wo bekommt man den eigentlich her?
Den offiziellen Standard nur für Geld bei der ISO oder bem ANSI. Den zur letzten Absegnung eingereichten Vorschlag bekommt man aber im Netz :-) Falls du ihn nicht findest, ich habe ihn als .ps, .pdf und .txt da.
Reinhard
lug-dd@mailman.schlittermann.de