Der Grund ist, dass es nicht erlaubt ist, dass in einer Anweisung dieselbe Variable mehrfach geschrieben wird.
Das ist nicht der Grund.
Zuerst muss der Begriff von sequenced und unsequenced geklärt werden.
Zwei Auswertungen (von bspw. Ausdrücken) A und B sind sequenced, wenn A immer vor B ausgewertet wird.
Given any two evaluations A and B, if A is sequenced before B, then the execution of A shall precede the execution of B. If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced.
[ Note: The execution of unsequenced evaluations can overlap. — end note ]
Man beachte auch die Notiz.
Nun heißt es:
The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.
Alle Operanden eines Operators werden immer vor dem Operator-Ausdruck selbst ausgewertet. Das trifft aber nur auf Wertberechnungen zu, nicht auf Seiteneffekte!
Außerdem:
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function.
[ Note: Value computations and side effects associated with different argument expressions are unsequenced. — end note ]
Es werden also alle Argumente einer Funktion, auch Default-Argumente, vor dem Aufruf ausgewertet, die Reihenfolge ist aber nicht festgelegt - und sie können sich auch überlappen.
Hier ist dann die goldene Regel bezüglich Skalaren:
If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
Ein Klassiker bezüglich sequencing ist
i = i++;
Hier gibt es zwei Seiteneffekte: Zuweisung und Inkrementieren. i++ wird zwar vor der Zuweisung berechnet, das nachträgliche Inkrementieren ist aber ein noch ausstehender Seiteneffekt.
Hier ist nicht festgelegt, ob i nach oder vor der Zuweisung inkrementiert wird, es ist also unsequenced. Daher: UB.
Anderes Beispiel:
i = (i++, i++, 8);
Das ist definiertes Verhalten, da in einer Liste von Ausdrücken jeder Seiteneffekt vor jeder Wertberechnung und jedem Seiteneffekt des nächsten Ausdrucks geschieht. Der Wert von i nach der Auswertung dieser Zuweisung ist 8.
Ideone: https://ideone.com/PhufJP
Der Ausdruck in diesem Code:
f( i++, i );
ist UB. Der Grund ist offensichtlich: Die value computation von i als zweites Argument und das nachträgliche Inkrementieren von i++ (welches ein Seiteneffekt ist) sind nicht sequenced.
Es gab schon Beispiele, welche nwp3s Pauschalregel widersprachen.
Hier ist noch einer:
f(a, (t=3, t=2), c);
Das ist definiert. Hingegen ist
f(a, (t=3, t=2), t);
UB.