3

Here's an example snippet:

int i = 4,b;    
b = foo(i++) + foo(i++);

I'm pretty certain it's not undefined, because there is a sequence point before the invocation of foo. However, if I compile the code with the -Wall flag a compiler warning is generated which says warning: operation on 'i' may be undefined. I realize it says may, but I'd just like to double check if I'm correct.

JustinBlaber
  • 4,629
  • 2
  • 36
  • 57
  • 2
    Whether it's defined or not, you shouldn't program like this. – Fiddling Bits Jan 09 '14 at 21:32
  • 1
    There's a sequence point before the call to `foo`, but there's no sequence point that's *necessarily* between the two evaluations of `i++`. The generated code could evaluate the first `i++`, then evaluate the second `i++`, then perform the two function calls, then add the results. C11 (see the [N1570 draft](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) changes the way this is described and may be clearer. – Keith Thompson Jan 09 '14 at 21:33
  • 1
    So it’s not actually undefined if ``foo`` doesn’t have side effects and does only depend on it’s argument if I get this correctly. – Jonas Schäfer Jan 09 '14 at 21:34
  • 2
    @JonasWielicki: No, it's undefined regardless, because `i` is modified twice with no intervening sequence point (in C99 terms). – Keith Thompson Jan 09 '14 at 21:35
  • So a compiler would be allowed to substitute ``b`` with any value it likes, like it can do if the expression would be directly using uninitialized values? Otherwise I’d just switch terms to *deterministic*, cause the output will always be the same with my assumptions (due to commutivity of ``+``). – Jonas Schäfer Jan 09 '14 at 21:36
  • @KeithThompson: There _is_ an intervening sequence point: One of the function calls (we just don't know which of them) -- interesting question, I'm not sure... – mafso Jan 09 '14 at 21:36
  • 1
    @mafso: As I said above, I believe both instances of `i++` could be evaluated before either function call takes place. [N1570](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) 6.5.2.2p10: "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." – Keith Thompson Jan 09 '14 at 21:44
  • A quibble: "ANSI C" often refers to the language defined by the 1989 ANSI C standard, but in fact ANSI has officially adopted the 2011 ISO C standard. If you're asking about ISO C (which is currently C11), you might want to update your title. – Keith Thompson Jan 09 '14 at 22:00

1 Answers1

7

The behavior is undefined.

b = foo(i++) + foo(i++);

As you say, there's a sequence point between the evaluation of the first i++ and the call to foo, and likewise between the evaluation of the second i++ and the call foo. But there isn't (necessarily) a sequence point between the two evaluations of i++, or more specifically between their side effects (modifying i).

Quoting the N1570 draft of the 2011 ISO C standard, section 6.5.2.2p10:

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.

The second sentence is significant here: the two evaluations of i++ are "indeterminately sequenced" with respect to the two function calls, meaning that they can occur either before or after the calls to foo. (They're not unsequenced, though; each of them occurs either before or after the calls, but it's unspecified which.)

And 6.5p2 says:

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.

Putting this together, a conforming implementation could evaluate the expression in this order:

  1. Evaluate the first i++ and save the value somewhere.
  2. Evaluate the second i++and save the value somewhere.
  3. Call foo, passing the first saved value as an argument.
  4. Call foo, passing the second saved value as an argument.
  5. Add the two results.
  6. Store the sum in b.

There is no sequence point between steps 1 and 2, both of which modify i, so the behavior is undefined.

(That's actually a slight oversimplification; the side effect of modifying i can be separated from the determination of the result of i++.

Bottom line: We know that

b = i++ + i++;

has undefined behavior, for reasons that have been explained repeatedly. Wrapping the i++ subexpressions in function calls does add some sequence points, but those sequence points don't separate the two evaluations of i++ and therefore don't cause the behavior to become well defined.

Even bottommer line: Please don't write code like that. Even if the behavior were well defined, it would be more difficult than it's worth to prove it and to determine what the behavior should be.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • I've read the part of the standard you copied and pasted like 3-4 times and still don't really understand it, although I feel that your interpretation is correct. Either way I won't program like this, I just posted this question to get a better understanding of sequence points. – JustinBlaber Jan 09 '14 at 23:23
  • 1
    If you need to understand sequence points, you are coding badly. – Martin James Jan 10 '14 at 01:08
  • @MartinJames It was a purely academic question. Just trying to understand C better. – JustinBlaber Jan 10 '14 at 03:56