The C standard does not define the behavior in this case, not because of the rule about unsequenced effects or explicit statement but rather because it fails to address the situation.
C 2011 (unofficial draft N1570) clause 6.7, paragraph 1, shows us the overall grammar of declarations. In this grammar, int a = a = 1;
is a
declaration in which:
int
is a declaration-specifiers which consists solely of the type-specifier int
.
a = a = 1
is an init-declarator, in which a
is a declarator and a = 1
is an initializer. The declarator consists solely of the identifier a
.
6.7.6 3 defines a full declarator to be a declarator that is not part of another declarator, and it says the end of a full declarator is a sequence point. However, these are not necessary for our analysis.
6.7.9 8 says “An initializer specifies the initial value stored in an object.”
6.7.9 11 says “The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.”
So, on one hand, the initializer, which has the value 1, specifies the initial value stored in a
. On the other hand, the expression a = 1
has the side effect of storing 1 in a
. I do not see anything in the C standard that says which occurs first. The rules about sequencing within expressions apply only to the evaluation of the initializer; they do not tell us the order of giving the “initial value” to a
and the side effect of assigning to it.
It is reasonable to conclude that, whether a
is given the initial value 1 or is assigned the value 1, it ends up with the value 1, so the behavior is defined. However, the standard famously makes it undefined behavior to modify the value of an object twice in an unsequenced way, even if the value being written is the same. The explicit statement of that rule is in 6.5 2, which applies to expressions, and hence does not apply in this situation where we have a conflict between an initialization and an expression. However, we might interpret the spirit of the standard to be:
- In order to afford an implementation opportunity to do whatever it needs to do to store (or modify) a new value in an object, a sequencing for the store relative to other stores (or modifications) must be defined.
- The standard fails to define a sequence for the initialization and the assignment side effect, and therefore it fails to afford the implementation this needed constraint.
Thus, the standard fails to specify the behavior in a way that guarantees an implementation will produce defined behavior.
Additionally, we can consider int a = 2 + (a = 1)
. In this case, the value of the initializer is 3, but the side effect assigns 1 to a
. For this declaration, the standard does not say which value prevails (except that one might interpret “initial value” literally, thus implying that 3 must be assigned first, so the side effect must be later).