24
int f() {
    static int i=0;
    return ++i;
}
int g() {
    return f() + f();
}

Does g() return 3 or is the result undefined?

0

5 Answers 5

18

Chapter and verse:

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).

3
  • If a+b run out of range int, b+a does so, thus a+b==b+a makes no sense but a+b and b+a do the same thing(if the whole UNDEFINED is treated same)
    – l4m2
    Commented Nov 10, 2016 at 16:53
  • @l4m2 - there's no guarantee that two different pieces of undefined code produce equivalent results even if they logically should. In your example, for instance, the compiler might trap an integer overflow exception and abort the calculation, returning false, and that would be considered a perfectly correct (and in some cases sensible) way of behaving. Fortunately, however, the code in the question isn't undefined, but unspecified, which gives the compiler a lot less latitude to change its behaviour. Commented Nov 11, 2016 at 10:01
  • Periata Breatta, so I said "a+b==b+a makes no sense" and "the whole UNDEFINED is treated same".
    – l4m2
    Commented Nov 14, 2016 at 12:43
15

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.

3
  • 1
    With regard to #4, it should perhaps be clarified that for a program to be correct, all possible combinations of Unspecified behaviors must yield output meeting the program's requirements. If a program is required to output either "foobar" or "barfoo", then 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.
    – supercat
    Commented Nov 10, 2016 at 19:05
  • @supercat Yes, but that doesn't happen here as all possible paths are correct.
    – 2501
    Commented Nov 10, 2016 at 19:08
  • 1
    In this case, yes, but with regard to footnote 4 I think it's important to recognize the general principle that if a program would fail to meet requirements for any possible combination of undefined behaviors, the program is incorrect.
    – supercat
    Commented Nov 10, 2016 at 19:12
11

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.

8
  • 2
    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().
    – sepp2k
    Commented Nov 10, 2016 at 16:33
  • 2
    @sepp2k: Not implementation defined, but indeterminate. Implementation defined implies the implementation has to specifcy the behaviour and guarantee it. But here the order of evaluation of the functions can vary in the same program. Commented Nov 10, 2016 at 16:34
  • 3
    The result cannot be indeterminate. The expressions are indeterminately sequenced and the result is unspecified, either 1 or -1.
    – 2501
    Commented Nov 10, 2016 at 16:49
  • 6
    @l4m2: Undefined Behavior and Unspecified aspects of behavior are totally different concepts. Undefined Behavior would mean that 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.
    – supercat
    Commented Nov 10, 2016 at 19:09
  • 5
    @l4m2 "undefined", "unspecified", "implementation defined", "indeterminate value", "indeterminately sequenced", "unsequenced", and a few other related terms have precise definitions in the C standard, and are not interchangeable.
    – zwol
    Commented Nov 11, 2016 at 2:32
1

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.

1

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.

Not the answer you're looking for? Browse other questions tagged or ask your own question.