Hallo!
In einem C#-Test sollte ich heute den Output von folgendem Stück Code herausfinden:
static void Main(string[] args)
{
var i = 5;
for (int j = 0; j < 5; j++)
{
i = i++;
}
Console.Write(i);
Console.ReadKey();
}
Die Antwort, denkbar einfach, sollte eigentlich 10 sein. Doch zu meiner großen Überraschung spuckt der Compiler eine 5 aus.
Der gleiche Code in C++
#include <iostream>
int main()
{
int i = 5;
for(int j = 0; j < 5; j++)
{
i = i++;
}
std::cout << i;
std::cin.get();
}
spuckt hingegen eine 10 aus. Ich verstehe schon allein nicht, warum C# 5 statt 10 ausgibt, denn die Variable i sollte doch in jedem Schleifendurchlauf um 1 erhöht werden...? Weiß jemand wieso sich der ++ Operator in C# so benimmt?
Hallo Grundkurs,
der Ausdruck
i++;
ist die Kurzform der folgenden Anweisung:
i = i + 1;
Sie erhöht den Wert der Variable i
um 1. Der Rückgabewert der Kurzform dieses Ausdrucks ist jedoch nicht der Wert der erhöhten Variable, sondern der der noch nicht erhöhten. Dieser Wert (5 in deinem Beispiel) wird zurückgegeben; die Variable i
wird erst anschließend inkrementiert. Der Wert der Variable bleibt in deinem Beispiel deshalb konstant 5, da du ihr den noch nicht erhöhten Wert zuweist und somit Durchlauf für Durchlauf effektiv nichts machst.
Um dein Beispiel zum Laufen zu bringen, lass einfach die Zuweisung weg und schreibe lediglich folgende Zeile:
i++;
i = ++i;
Dabei wird der Wert von i
erst erhöht und anschließend (redundant, da doppelt) zugewiesen. Das dient jedoch nur zu Illustrationszwecken und ist nicht zu empfehlen.
m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Hat jemand eine Erklärung dafür, weshalb C++ das anders macht als C#?
Hallo 7.e.Q,
je nach Anwendungsfall kann es durchaus sinnvoll sein, über beide Varianten der Inkrementierung zu verfügen, wie der folgende Pseudocode zeigt:
int i = 0;
object[] demoArray = new object[...];
foreach (...)
{
demoArray[i++] = ...
}
Würde der erhöhte Wert der Variablen zurückgegeben werden, würde die erste Zuweisung bereits den Index 1 verwenden und nicht, wie gewünscht, den Index 0.
Das Ganze nennt sich übrigens Prä- bzw. Post-Inkrementierung. Analog dazu gibt es ebenfalls die Prä- bzw. Post-Dekrementierung mit dem Operator --
.
m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Hallo m0rius!
Danke erstmal für die ausführliche Antwort und die Erklärung, warum im C# Code i nicht erhöht wird. Die Frage von 7.e.Q bezieht sich darauf, wieso der gleiche Code, bei dem in C# die Variable immer "auf der Stelle tritt", in C++ hingegen doch erhöht wird. Das ist halt auch seltsam. Ich meine der Code ist wirklich identisch (die kleinen Syntax-Anpassungen ausgenommen).
Hallo Grundkurs,
ohne mich jetzt genauer mit der Dokumentation von C++ beschäftigt zu haben, vermute ich, dass die Post-Inkrementierung in C++ so implementiert ist wie die Prä-Inkrementierung in C#. Dies wurde, schätze ich, aus o.g. Gründen in C# abgeändert.
m0rius
Nein, Grundsätzlich gibt es auch in C++ die Unterscheidung zwischen Pre- und Post-Increment.
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Ich habe endlich rausgefunden warum c++ den Wert 10 ausgibt und c# den Wert 5:
Side effect operators (++, --, =, +=, -=, *=, /=, %=, &=, |=, ^=, <≤, and >≥) may cause unexpected results if they are used on the same variable or memory location more than once in the same expression. The order in which side effects occur within an expression is not specified.
Das steht hier: KB50694: INFO: Evaluation Order of Expression and Function Args Undefined
Wenn ich das also richtig verstehe: Inkrement-oder Dekrement-Operatoren können, soweit die Variable mehr als einmal in der gleichen Anweisung benutzt wird, zu nicht spezifiziertem Verhalten führen.
Auf der Seite wird z.B. der Befehl
a[i] = i++; /* UNDEFINED */
als Beispiel für undefiniertes Verhalten angeführt, der meinem von oben fast gleicht.
Hallo Grundkurs,
klar ist, der Wert von i (im Speicher) wird abgerufen (z.B. in ein Prozessorregister) und wieder an i (im Speicher; aus dem Prozessorregister) zugewiesen. Und es ist klar, dass der Wert von i (im Speicher) nach dem Abrufen erhöht wird. Die entscheidenden Frage ist, wird er sofort nach dem Abrufen erhöht (so wie bei C#) oder erst ganz am Ende, nachdem alle anderen Operationen (also auch die Zuweisung) durchgeführt wurde (so wie in C++). Undefiniert ist also nur, zu welchem Zeitpunkt nach dem Abrufen der Wert von i erhöht wird. Sicher ist, dass er erst nach dem Abrufen erhöht wird. Meine Erwartung (sowohl in C# als auch in C++) wäre gewesen, dass der Wert sofort nach dem Abrufen erhöht wird. Der Thread hat mir gezeigt, dass man in C++ davon doch nicht ausgehen kann.
C#:
Wert von i abrufen (5)
i erhöhen (6)
Abgerufenen Wert (5) an i zuweisen (5)
C++:
Wert von i abrufen (5)
Abgerufenen Wert (5) an i zuweisen (5)
i erhöhen (6)
herbivore
Ah, alles klar, unterschiedliches (bzw. bei C# überhaupt erst vorhandenes) Speichermanagement ist also der Grund für das unterschiedliche Verhalten zwischen C++ und C#. Wieder was gelernt.
Hallo,
Der Ausdruck:
i = i++;
ist auch bei C/C++ undefiniert. Es gibt hier keinen Sequenzpunkt, aber der Wert der Variablen wird zweimal verändert. Der Standard erlaubt aber nur eine Änderung zwischen zwei Sequenzpunkten. Es ist also purer Zufall, dass bei Grundkurs "10" herausgekommen ist. Es ist ebenfalls denkbar, dass "5", wie beim C#-Beispiel, herauskommt.
Gruß
wolpertinger