Betrachten Sie das folgende C-Programm:Ist das Verhalten von i = post_increment_i() angegeben, nicht angegeben oder nicht definiert?
int i = 0;
int post_increment_i() { return i++; }
int main() {
i = post_increment_i();
return i;
}
Im Hinblick auf die Version 2011 der C-Standard (als C11 bezeichnet), die der folgenden Alternativen zutrifft:
- C11 garantiert, dass Haupt Renditen 0.
- C11 garantiert, dass Main entweder 0 oder 1 zurückgibt.
- Das Verhalten dieses Programms ist nach C11 nicht definiert.
Relevant Schnipsel aus dem Standard-C11:
5.1.2.3 Programmausführung
ein flüchtiges Objekt Zugriff auf ein Objekt zu modifizieren, um eine Datei zu modifizieren, oder eine Funktion aufrufen, die Ist eine dieser Operationen alle Nebeneffekte, die Änderungen im Zustand der Ausführungsumgebung sind. Die Auswertung eines Ausdrucks umfasst im Allgemeinen sowohl den Wert Berechnungen als auch die Einleitung von Nebenwirkungen. Die Wertberechnung für einen lvalue-Ausdruck umfasst das Bestimmen der Identität des designierten Objekts.
Vorangeordnet ist eine asymmetrische, transitive, paarweise Beziehung zwischen den Auswertungen , die von einem einzigen Thread ausgeführt wird, was eine partielle Ordnung zwischen diesen Auswertungen induziert. Gegeben zwei beliebige Auswertungen A und B, wenn A vor B sequenziert wird, dann muss die Ausführung von A der Ausführung von B vorausgehen. (Umgekehrt, wenn A vor B sequenziert wird, dann ist B nach A sequenziert) wird nicht vor oder nach B sequenziert, dann sind A und B nicht sequenziert. Auswertungen A und B werden indeterminately sequenziert wenn A sequenziert entweder vor oder nach B, aber es ist nicht spezifiziert, welche. Das Vorhandensein eines Sequenzpunkts zwischen der Auswertung der Ausdrücke A und B impliziert, dass jede mit A assoziierte Wertberechnung und Nebenwirkung vor jeder Wertberechnung und der mit B assoziierten Nebenwirkung sequenziert wird. (Eine Zusammenfassung der Sequenz Punkte werden im Anhang C angegeben)
13) Die Ausführungen von unsequenced Auswertungen verschachteln können. Unbestimmt sequenzierte Auswertungen können nicht verschachtelt werden, sondern können in beliebiger Reihenfolge ausgeführt werden.
6,5 Expressions
Ein Ausdruck ist eine Folge von Operatoren und Operanden, die Berechnung eines Wert gibt, oder dass bezeichnet ein Objekt oder eine Funktion oder das erzeugt Nebenwirkungen oder dass führt eine Kombination davon durch. Die Wertberechnungen der Operanden eines Operators werden, bevor der Wert Berechnung des Ergebnisses des Bedieners sequenziert.
Wenn eine Nebenwirkung auf einem skalares Objekt unsequenced relativ zu entweder einer anderen Nebenwirkung auf dem gleichen skalare Objekt oder einen Berechnungswert den Wert des gleichen skalare Objekts verwendet wird, ist das Verhalten undefiniert. Wenn mehrere zulässige Ordnungen derTeilausdrücke eines Ausdrucks vorhanden sind, ist das Verhalten undefiniert, wenn ein solcher nicht sequenzierter Effekt in einer der Reihenfolgen auftritt.
6.5.2.2 Funktionsaufrufe
Es gibt einen Sequenzpunkt nach den Auswertungen der Funktion Bezeichner und die tatsächlichen Argumenten aber vor dem eigentlichen Anruf. Jede Auswertung in der aufrufenden Funktion (einschließlich anderer Funktionsaufrufe), die vor oder nach der Ausführung des Rumpfes der aufgerufenen Funktion sonst nicht speziell sequenziert wird, ist bezüglich der Ausführung der aufgerufenen Funktion bezüglich unbestimmt sequenziert.
94) Mit anderen Worten, Funktionsausführungen verschachteln sich nicht miteinander.
6.5.2.4 Postfix Inkrement- und Dekrement-Operatoren
Das Ergebnis der Postfix ++ Operator ist der Wert des Operanden. Als Nebeneffekt wird der Wert des Operandenobjekts inkrementiert (dh der Wert 1 des entsprechenden Typs wird hinzugefügt). [...] Die Wertberechnung des Ergebnisses wird vor dem Nebeneffekt zur Aktualisierung des gespeicherten Operandenwertes sequenziert. In Bezug auf einen unbestimmt sequenzierten Funktionsaufruf ist die Operation von Postfix ++ eine einzelne Auswertung.
6.5.16 Zuordnungen
einen Zuweisungsoperator speichert einen Wert in dem Objekt durch den linken Operanden bezeichnet. [...] Der Nebeneffekt, den gespeicherten Wert des linken Operanden zu aktualisieren, ist , der nach den Wertberechnungen des linken und rechten Operanden sequenziert wurde. Die Auswertungen der Operanden sind nicht sequentiell.
6,8 Statements und blockiert
Ein voller Ausdruck ist ein Ausdruck, der nicht Teil eines anderen Ausdrucks oder eines declarator ist. Jeder der folgenden Ausdrücke ist ein vollständiger Ausdruck: [...] der Ausdruck in einer Ausdruckanweisung; [...] der (optionale) Ausdruck in einer Rückgabe Anweisung. Es gibt einen Sequenzpunkt zwischen der Auswertung eines vollständigen Ausdrucks und der Auswertung des nächsten auszuwertenden vollständigen Ausdrucks.
Die drei Alternativen oben entsprechen den folgenden drei Fälle dargestellt:
- Der Nebeneffekt der Postfix Inkrementoperator wird in Haupt vor der Zuweisung sequenziert.
- Der Nebeneffekt des postfix-Inkrementoperators wird vor oder nach der Zuweisung in main sequenziert, und in C11 wird nicht angegeben, welches. (Mit anderen Worten, die zwei Nebenwirkungen sind unbestimmt sequenziert.)
- Die zwei Nebenwirkungen sind nicht sequenziert.
Es scheint, dass die erste Alternative, die durch die folgende Argumentationskette gilt:
Betrachten wir die Regel Jede Auswertung in der Aufruffunktion (einschließlich andere Funktionsaufrufe), die nicht anderweitig ausdrücklich ist sequentiell vor oder nach der Ausführung des Körpers der aufgerufenen Funktion ist in Bezug auf der Ausführung der aufgerufenen Funktion unbestimmt sequenziert. in 6.5.2.2. Annahme A: Der Nebeneffekt des Zuweisungsoperators in main ist eine solche "Auswertung". Annahme B: Der Ausdruck "die Ausführung der aufgerufenen Funktion" umfasst sowohl die Wertberechnung des Postfix-Inkrement-Operators als auch die Nebenwirkung des Postfix-Inkrement-Operators. Aus diesen Annahmen und der obigen Regel folgt, dass entweder I) die Wertberechnung und die Nebenwirkung des Postfix-Inkrementoperators beide vor dem Nebeneffekt des Zuweisungsoperators in main oder II) der Wertberechnung und dem Nebeneffekt sequenziert werden des Postfix-Inkrementoperators werden beide nach dem Nebeneffekt des Zuweisungsoperators in main sequenziert.
Betrachten Sie die Regel Der Nebeneffekt der Aktualisierung des gespeicherten Wertes des linken Operanden ist nach den Wertberechnungen der linken und rechten Operanden. Diese Regel schließt Fall I oben aus. Daraus folgt, dass Fall II gilt. QED
Insgesamt sieht dies wie ein ziemlich starkes Argument aus. Es entspricht auch dem, was man intuitiv als die wahrscheinlichste Alternative ansehen würde.
Es ist jedoch auf spezifische Interpretationen der Begriffe "Auswertung" und "Ausführung der aufgerufenen Funktion" (Annahmen A und B) und eine nicht ganz geradlinige Argumentationskette angewiesen, also wollte ich es dort herausbringen Sehen Sie, ob die Leute Grund zu der Annahme haben, dass diese Interpretation falsch ist. Man beachte, dass die Fußnote 94 dieser Interpretation nur dann entspricht, wenn sie auch in dem Sinne gilt, dass der Anrufer nicht mit dem Angerufenen verschachtelt, was wiederum bedeutet, dass "verschachteln" Verschachteln im "abab" -Sinn bedeutet, da sich offensichtlich ein Anrufer verschachtelt der Angerufene im schwächeren "aba" -Sinn. Außerdem erscheinen die Alternativen 2 und 3 in einem Szenario plausibel, in dem der Compiler die Funktion einfügt und dann die gleichen Optimierungen durchführt, die motivieren, warum der Ausdruck i = i++
ein undefiniertes Verhalten hat.
pas de probleme. – wildplasser