Consider the following C program:
int i = 0; int post_increment_i() { return i++; } int main() { i = post_increment_i(); return i; }
Regarding the 2011 version of the C standard (version C11), which of the following alternatives matters:
- C11 ensures that main returns 0.
- C11 ensures that main returns 0 or 1.
- The behavior of this program is undefined according to C11.
Relevant fragments from the C11 standard:
5.1.2.3 program execution
Accessing a volatile object, changing an object, changing a file, or calling a function that any of these operations are all side effects that are changes in the state of the runtime. Evaluation of expression as a whole includes both the value of computing and initiating side effects. Computing values for an lvalue expression involves determining the identity of the assigned object.
The previously described is an asymmetric, transitional, pairwise relation between the estimates performed by a single thread, which causes a partial order among these estimates. For any two evaluations, A and B, if A is sequenced before B, then the execution of A precedes the execution of B. (Conversely, if A is sequenced to B, then B is the sequence after A.) If A is not sequenced before or after B, then A and B are unsequenced. Scores A and B are indefinitely sequenced when A is sequenced either before or after B, but that is not indicated. 13 . The presence of a sequence point between the evaluation of expressions A and B implies that each calculation of the value and the side effect associated with A are sequenced before each value and the side effect associated with B. (A brief description of the points in the sequence is given in Appendix C.)
13) Performance of imperceptible assessments may alternate. Uncertainly ordered estimates cannot alternate, but can be performed in any order.
6.5 Expressions
An expression is a sequence of operators and operands that sets the calculation to a value or designates an object or function, or generates side effects, or that performs a combination of them. The calculation of the values of the operands of the operator is ordered before the calculation of the value of the result of the operator.
If the side effect of a scalar object is independent of another side effect on the same scalar object or calculating a value using the value of the same scalar object, the behavior is undefined. If there are several valid orders of expression subexpression, the behavior is undefined, if such an inconsistent side of the effect occurs in any of the orderings.
6.5.2.2 Functional calls
After evaluating the function designation, there is a sequence point and the actual arguments, but before the actual call. Each estimate in the calling function (including other function calls) that is not otherwise sequenced before or after the execution of the body of the called function is indefinitely ordered with respect to the execution of the called function. 94
94) In other words, the execution of functions does not "alternate with each other."
6.5.2.4 Postfix increment and decrement operators
The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is increased (that is, a value of 1 of the corresponding type is added to it). [...] The calculation of the result value is sequenced before the side effect of updating the stored value of the operand. As for the indefinitely sequenced function call, the postfix ++ operation is a separate assessment.
6.5.16 Appointments
The assignment operator stores the value in the object indicated by the left operand. [...] A side effect of updating the stored value of the left operand is ordered after calculating the values of the left and right operands. Estimates of operands are not affected.
6.8. Expressions and Blocks
A full expression is an expression that is not part of another expression or declarator. Each of the following is a complete expression: [...] an expression in an expression of an expression; [...] an (optional) expression in the reverse expression. There is a point in the sequence between evaluating the full expression and evaluating the next full expression to evaluate.
The three alternatives above correspond to the following three cases:
- The side effect of the postfix increment operator is sequenced before the assignment basically.
- A side effect of the postfix increment statement is sequenced either before or after the assignment is mainly, and C11 does not indicate which one. (In other words, the two side effects are indefinitely sequenced.)
- Two side effects have no consequences.
It seems that the first alternative is fulfilled by the following chain of reasoning:
Consider the rule Each estimate in the calling function (including other function calls) that is not otherwise sequenced before or after the execution of the body of the called function is indefinitely ordered with respect to the execution of the called function. in 6.5.2.2. Assumption A: A side effect of the assignment operator is basically such an “estimate”. Assumption B: The phrase “executing the called function” includes both calculating the value of the postfix increment operator and the side effect of the postfix increment operator. From these assumptions and the above rule, it follows that either I) the calculation of the value and the side effect of the postfix increment operator are both sequented before the side effect of the assignment operator basically, or II) the calculation of the value and the side effect of the postfix increment operator are sequentially ordered after the side effect of the assignment operator in mostly.
Consider the rule. A side effect of updating the stored value of the left operand is after calculating the values of the left and right operands. This rule excludes the case above. This implies case II. QED
Overall, this seems like a pretty strong argument. In addition, this is consistent with intuitively considering the most likely alternative.
However, he relies on specific interpretations of the terms “evaluation” and “execution of the called function” (assumptions A and B) and a not quite straight line of reasoning, so I wanted to put it there to see if people have reason to believe that this interpretation is wrong. Note that footnote 94 is equivalent to this interpretation only if it is also applied in the sense that the caller does not alternate with the callee, which in turn implies that “interlace” means interlace in the sense of “abab” , since, obviously, the calling element alternates with the called in the weaker sense of "aba." Alternatively, alternatives 2 and 3 look plausible in a scenario where the compiler builds a function and then performs the same kinds of optimizations that motivate why the i = i++
expression has undefined behavior.
source share