Oft wird diese Frage als Duplikat von Fragen zum Code verknüpft wie
printf("%d %d\n", i, i++);
o
printf("%d %d\n", ++i, i++);
oder ähnliche Varianten.
Dies ist zwar auch undefiniertes Verhalten wie bereits erwähnt, gibt es feine Unterschiede, wenn printf()
bei einem Vergleich mit einer Aussage wie der folgenden:
x = i++ + i++;
In der folgenden Erklärung:
printf("%d %d\n", ++i, i++);
die Reihenfolge der Bewertung von Argumenten in printf()
est nicht spezifiziert . Das heißt, Ausdrücke i++
y ++i
können in beliebiger Reihenfolge ausgewertet werden. C11-Norm enthält einige einschlägige Beschreibungen dazu:
Anhang J, nicht spezifizierte Verhaltensweisen
Die Reihenfolge, in der der Funktionsbezeichner, die Argumente und die Unterausdrücke innerhalb der Argumente in einem Funktionsaufruf ausgewertet werden (6.5.2.2).
3.4.4, nicht spezifiziertes Verhalten
Verwendung eines nicht spezifizierten Wertes oder ein anderes Verhalten, bei dem dieser Internationale Norm zwei oder mehr Möglichkeiten vorsieht und keine weiteren Anforderungen an die jeweils zu wählende Variante stellt.
BEISPIEL Ein Beispiel für nicht spezifiziertes Verhalten ist die Reihenfolge, in der die Argumente einer Funktion ausgewertet werden.
El nicht näher bezeichnetes Verhalten selbst ist KEIN Thema. Betrachten Sie dieses Beispiel:
printf("%d %d\n", ++x, y++);
Auch dies hat nicht näher bezeichnetes Verhalten weil die Reihenfolge der Auswertung von ++x
y y++
ist nicht spezifiziert. Aber es ist eine völlig legale und gültige Aussage. Es gibt keine undefiniertes Verhalten in dieser Anweisung. Da die Änderungen ( ++x
y y++
) werden durchgeführt, um deutlich Objekte.
Was besagt die folgende Aussage
printf("%d %d\n", ++i, i++);
als undefiniertes Verhalten ist die Tatsache, dass diese beiden Ausdrücke die dieselbe Objekt i
ohne ein dazwischenliegendes Sequenzpunkt .
Ein weiteres Detail ist, dass die Komma die an dem printf()-Aufruf beteiligt ist, ist eine Abscheider , nicht die Komma-Operator .
Dies ist eine wichtige Unterscheidung, da die Komma-Operator führt eine Sequenzpunkt zwischen der Auswertung ihrer Operanden, so dass das Folgende legal ist:
int i = 5;
int j;
j = (++i, i++); // No undefined behaviour here because the comma operator
// introduces a sequence point between '++i' and 'i++'
printf("i=%d j=%d\n",i, j); // prints: i=7 j=6
Der Komma-Operator wertet seine Operanden von links nach rechts aus und gibt nur den Wert des letzten Operanden wieder. Also in j = (++i, i++);
, ++i
Inkremente i
a 6
y i++
ergibt den alten Wert von i
( 6
), der zugewiesen ist j
. Dann i
wird 7
aufgrund der Nacherhöhung.
Wenn also die Komma im Funktionsaufruf ein Komma-Operator sein sollte, dann
printf("%d %d\n", ++i, i++);
wird kein Problem sein. Aber es ruft auf undefiniertes Verhalten weil die Komma hier ist ein Abscheider .
Für diejenigen, die neu sind bei undefiniertes Verhalten von der Lektüre profitieren würden Was jeder C-Programmierer über undefiniertes Verhalten wissen sollte um das Konzept und viele andere Varianten von undefiniertem Verhalten in C zu verstehen.
Dieser Beitrag: Unbestimmtes, nicht spezifiziertes und implementierungsdefiniertes Verhalten ist ebenfalls relevant.
16 Stimmen
@Jarett, nein, ich brauchte nur ein paar Hinweise auf "Sequenzpunkte". Bei der Arbeit fand ich ein Stück Code mit i = i++, ich dachte "Das ändert den Wert von i nicht". Ich habe es getestet und mich gefragt, warum. Inzwischen habe ich diese Anweisung entfernt und durch i++ ersetzt;
232 Stimmen
Ich finde es interessant, dass jeder IMMER davon ausgeht, dass solche Fragen gestellt werden, weil der Fragesteller das fragliche Konstrukt BENUTZEN will. Meine erste Vermutung war, dass PiX weiß, dass diese Konstrukte schlecht sind, aber neugierig ist, warum sie sich so verhalten, egal welchen Compiler er/sie benutzt... Und ja, was unWind sagte... es ist undefiniert, es könnte alles tun... einschließlich JCF (Jump and Catch Fire)
41 Stimmen
Ich bin neugierig: Warum warnen Compiler anscheinend nicht vor Konstrukten wie "u = u++ + ++u;", wenn das Ergebnis undefiniert ist?
0 Stimmen
Lesen Sie auch: stackoverflow.com/questions/13615243/
0 Stimmen
Warum sollten Sie erwarten, dass i = (i++) gleich 1 ist? Klammern setzen die natürliche Reihenfolge der Auswertung außer Kraft, so dass alles, was innerhalb von Klammern steht, zuerst geschieht. Also wird i++ zuerst passieren (natürlich würde i++ nach der Zuweisung passieren), wodurch es 2 wird. 2 würde dann wieder i zugewiesen werden. i ist 2.
5 Stimmen
(i++)
ergibt unabhängig von den Klammern immer noch den Wert 13 Stimmen
Was auch immer
i = (i++);
sollte, gibt es sicherlich einen klareren Weg, es zu schreiben. Das wäre selbst dann der Fall, wenn es gut definiert wäre. Selbst in Java, das das Verhalten voni = (i++);
ist es immer noch ein schlechter Code. Einfach schreibeni++;
0 Stimmen
Nur meine Meinung: solche Anweisungen sind ein undefiniertes Verhalten, weil Sie dieselbe Variable (Speicherplatz) lesen und schreiben. Dadurch kann der Compiler eine "Optimierung" durchführen, d. h. er räumt Ihren eigenen Mist auf. Natürlich bringt dies einige Einschränkungen mit sich. Das Lesen aus dem Speicher ist LANGSAM, daher werden Register verwendet und dann mit dem echten Speicher synchronisiert. Der Nebeneffekt ist, dass der Compiler nun nicht mehr weiß, welchen Wert er verwenden soll: den aus dem Speicher oder den im Register, der bereits geändert wurde. (Ich erkläre es gerne so, das macht am meisten Sinn)
3 Stimmen
@LearnOpenGLES: Das tun sie.
1 Stimmen
Ich habe einen Artikel geschrieben über Erkennung von undefiniertem Verhalten in Ausdrücken die viele ähnliche Beispiele abdeckt, aber in Bezug auf die C++11-Regeln für die Sequenzierung definiert ist. Vielleicht ist es für einige der Leser hier nützlich.
1 Stimmen
Das erinnert mich an die Fragen, die viele Softwarefirmen in Indien bei Vorstellungsgesprächen stellen. Obwohl das Verhalten undefiniert ist, versuchen sie immer noch, der Ausgabe eine Logik aufzuerlegen. Ähnliche Fragen werden auch in vielen C-Büchern von Yashwant Kanetkar gestellt. Diese Art von Fragen machen mich wirklich krank :(
1 Stimmen
@LearnOpenGLES: Mein Compiler (gcc 4.8.1) warnt mich bei Konstrukten wie u=u++ & j=i++ + ++i;
2 Stimmen
Auch wenn es in dieser Frage um
C
Es könnte für einige Aspekte von Interesse sein, dass sich dies mit der nächsten Version vonC++
mit den gewählten Garantien für die C++17-Auswertungsreihenfolge (P0145R2) Mehr: stackoverflow.com/questions/38501587/0 Stimmen
Wie in einigen Kommentaren erwähnt, gibt es in C / C++ keine expliziten Regeln für die Reihenfolge der Auswertung. Der ungewöhnlichste Fall ist APL (A Programming Language), die Ausdrücke von rechts nach links auswertet (was mehrere Zuweisungen in einer einzigen Zeile ermöglicht), wobei Klammern verwendet werden, um die Reihenfolge der Auswertung außer Kraft zu setzen.
0 Stimmen
C-Compiler liefern andere Ergebnisse als Java-Compiler:
int i=5; System.out.printf(",%d,%d,%d,%d,%d",i++,i--,++i,--i,i);
gcc 5.3.0: Output: 4,5,5,5,5,5 Java1.8 Ausgabe: 5,6,6,5,50 Stimmen
@i_am_zero: Die Tatsache, dass der Standard ein Verhalten in einer bestimmten Situation nicht vorschreibt, bedeutet nicht, dass keine Implementierungen so detailliert spezifizieren, wie sie den Code verarbeiten, dass nur ein mögliches Verhalten mit der Spezifikation vereinbar ist. Ein Problem des Standards ist, dass er nie versucht hat, alle Fälle zu katalogisieren, in denen eine Implementierung einen Umweg gehen müsste pas sich in vorhersehbarer Weise zu verhalten (z. B. durch
memcpy
in Fällen, in denen Quelle und Ziel gelegentlich gleich sind, z.B. weil die Kosten einer gelegentlichen redundanten Kopie geringer sind als...0 Stimmen
...die Kosten für die Kontrolle von jede Operation, ob die Kopie notwendig war). IMHO wäre der Standard besser, wenn er ein grundlegendes Ausführungsmodell spezifizieren würde und dann die Arten von Optimierungen, die Programmierer aktivieren können. Gegeben
x=(*p)++ + (*q)++; b=*p; c=*p;
Zum Beispiel kann es vernünftig sein, zu sagen, dass ein Compiler, wenn einige Optimierungen aktiviert sind, nach eigenem Ermessen Folgendes behandeln kannb
yc
entweder die Eins plus den Wert, der vor dem Inkrement von*p
oder einen Wert enthalten, der aus*p
zu einem beliebigen Zeitpunkt zwischen der Erhöhung und der Zuordnung zub
oc
.0 Stimmen
@i_am_zero: Solche Regeln würden den Compilern fast die gesamte nützliche Flexibilität geben, die sie unter dem gegenwärtigen Standard haben, aber wenn sie mit Möglichkeiten kombiniert werden, unbestimmte Werte in beliebige Werte zu konvertieren, könnten einige Arten von Code effizienter geschrieben werden, als es derzeit möglich ist.