int f() {
static int i=0;
return ++i;
}
int g() {
return f() + f();
}
Does g()
return 3
or is the result undefined
?
6.5.2.2 Function calls
...
10 There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.94)
94) In other words, function executions do not ‘‘interleave’’ with each other
Upshot is that there is a sequence point between each ++i
by virtue of it being part of a function call. Thus, this behavior is well-defined.
Whether it actually does what you intend is another matter. Note that at some point you risk signed overflow, which is undefined. And as others have pointed out, f() - f()
may not give the result you would expect (left to right evaluation is not guaranteed in that case).
The evaluations of the two operands of the + operator are unsequenced1.
There is a sequence point just before the actual function call2. This sequence point is enough to separate the modifications of static variable i, making the entire expression indeterminately sequenced, and the order of the function calls unspecified3.
Thus the behavior remains defined, and the first call to function g will always yield 3, since the unspecified order of the function calls doesn't influence the result.
A program containing unspecified behavior is defined4.
(All quotes are from: ISO/IEC 9899:201x)
1 (6.5 Expressions 3)
Except as specified
later, side effects and value computations of subexpressions are unsequenced.
2 (6.5.2.2 Function calls 10)
There is a sequence point after the evaluations of the function designator and the actual
arguments but before the actual call.
3 (5.1.2.3 Program execution 3)
Evaluations A and B are indeterminately sequenced when A is sequenced
either before or after B, but it is unspecified which.
4 (4. Conformance 3)
A program that is correct in all other aspects, operating on correct data, containing
unspecified behavior shall be a correct program and act in accordance with 5.1.2.3.
printf("foo")+printf("bar");
would be a correct way to achieve that. If the program were required to output "foobar" and not "barfoo", then the same code would be incorrect unless an implementation offers guarantees beyond what the Standard requires.
There is no reason for this to be undefined, because +
operation is commutative, and because there are sequence points for the two ++
operations to be sequenced.
C standard has sequence points after a full expression, and also before a function is entered in a function call. Therefore, the results of ++
will be fully sequenced. Moreover, since +
is commutative, the order of calls to f()
does not change the result.
Note that the same logic would not apply to
return f() - f();
because -
is not commutative. The result of the expression above is unspecified, i.e. a standard-compliant compiler could reasonably produce a 1
or a -1
, depending on the order in which the compiler calls the two f()
functions.
f() - f()
still wouldn't be UB though, just implementation defined. That is, the part of your explanation that's about sequence points, still applies to f() - f()
.
int i=0; printf("%d", i++ - i++);
could not only output -1, output 0, or output 1, it could also output "Fred" or negate the laws of time and causality. By contrast, f() - f()
will either output 1 or output -1--it cannot do anything else.
There is no reason for undefined behavior. The static variable will be stored in .BSS memory space and no copies of it will be made - the compiler will handle this. The f()
function will be called twice, sequentially. The following case is similar:
for (int i = 0; i < 2; ++i)
g += f();
If the f()
function would have been called be two threads, that would have resulted in some undefined behavior.
Here is a quote from C11 Section 6.5 Paragraph 2:
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.84
But by Section 5.1.2.3 Paragraph 3, the ordering is indeterminately sequenced, not unsequenced, so the first sentence above does not apply. The second sentence only applies if there are unsequenced side effects in any of the orderings. But in each of the orderings there are no unsequenced side effects.
The C11 standard seems to be silent about the effect of indeterminately sequenced computations. Perhaps we are to conclude that it is acceptable for a conforming compiler to produce outputs of any of the possible sequences. In this interpretation there are 2 possible sequences, both of which result in g() returning the value 3.
So I think that this is acceptable.
Note that in C99 there is no section corresponding to Section 5.1.2.3 in C11. Section 5.5 Paragraph 3 says that the ordering of subexpressions and the order in which side effects take place are both unspecified.
"Unspecified" is not the same as "undefined".
This leads me to believe that in C99 this is not undefined behavior.