4

I'm trying to pin down my understanding of sequence points in C -- just wanted to check something. At present, I believe that (1) is undefined whereas (2) is merely unspecified, on the basis that in (2), there are sequence points after evaluating the arguments for g and h (so we're not modifying i twice between sequence points), but the order of evaluation of the arguments of f is still unspecified. Is my understanding correct?

#include <stdio.h>

int g(int i) {
    return i;
}

int h(int i) {
    return i;
}

void f(int x, int y) {
    printf("%i", x + y);
}

int main() {
    int i = 23;
    f(++i, ++i); // (1)
    f(g(++i), h(++i)); // (2)
    return 0;
}

EDIT:

It seems the key point here is whether the compiler is free to perform both the increments before either g or h is called -- my understanding from the answers below is that it is, although I'd appreciate confirmation that that's the case.

Stuart Golodetz
  • 20,238
  • 4
  • 51
  • 80
  • 2
    @MichaelDorgan: I won't :) I'm working on a static analysis tool to help stop people doing this sort of thing and the distinction might matter. – Stuart Golodetz Jun 12 '12 at 15:59

2 Answers2

12

Incorrect. Sequence points specify a partial order on the allowed ordering of operations. In case (2), there are sequence points:

  1. At the semicolon at the end of the line (1)
  2. After the evaluation of the arguments of g (i.e. the ++i) but before the call to g
  3. After the evaluation of the arguments of h (i.e. the ++i) but before the call to h
  4. After the evaluation of the arguments of f (i.e. after f and g have returned) but before the call to f
  5. After the return from f

So the partial order looks like this, from top to bottom:

    1
   / \
  /   \
 2     3
  \   /
   \ /
    4
    |
    | 
    5

2 and 3 are not ordered with respect to each other, since the order of evaluation of arguments is unspecified. Since i gets modified twice between the sequence points 1 and 4, the behavior is undefined.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • Ah right, glad I checked! I was aware of the fact that sequence points are a partial order, but I was thinking as follows - since the order of argument evaluation is unspecified, you get either 1 -> 2 -> 3 -> 4 -> 5 or 1 -> 3 -> 2 -> 4 -> 5. In either case, I figured there was a sequence point between 1 and 4 (at either 2 or 3). Seems like I missed the point entirely. – Stuart Golodetz Jun 12 '12 at 15:45
  • 1
    I don't disagree with this approach, but I think it doesn't immediately follow from the standard; 6.5 2 says that an object can be modified at most once between *adjacent* sequence points, but 1 and 4 are nonadjacent in the above graph. Is the implementation required to behave as-if the sequence point graph is linearised? – ecatmur Jun 13 '12 at 09:01
  • @ecatmur: Hmm, good point. I looked over the standard and I can't quite figure it out either. C99 §6.5/2 says "Between the previous and next sequence point...", but it's not clear what the "next sequence point" is after #1 -- the order of evaluation of `g` and `h` is unspecified, and they each have a sequence point after evaluating their arguments but before calling them, and those two sequence points occur before #4. – Adam Rosenfield Jun 13 '12 at 15:34
  • @AdamRosenfield: That's the crux of my misunderstanding about this - if it were definitely the case that the whole of `g(++i)` was evaluated first, or the whole of `h(++i)` was, then there'd be a sequence point between the two increments in either case and the behaviour would seemingly not be undefined (although perhaps it would in a formal sense). But if the increments can both happen before calling either `g` or `h`, then the behaviour would evidently be undefined rather than unspecified. My current understanding is that that's the point here. – Stuart Golodetz Jun 14 '12 at 01:23
  • @Stuart: Although the standard doesn't say this explicitly, I wonder if it could be interpreted to mean "If there is a permitted ordering of side effects and sequence points which would result in an object being modified more than once between sequence points, the behavior is undefined"? Because with that wording, the behavior would clearly be undefined, since the compiler could put both `++i`'s before calling either `g` or `h` (though it's not required to do so). – Adam Rosenfield Jun 14 '12 at 21:20
  • @AdamRosenfield: I certainly agree that if that wording were there and if it was definitely the case that the increments can both happen before a call to `g` or `h`, then the behaviour would be clearly undefined. I think I'm also happy to make the assumption that it's undefined just if both the increments can happen first (i.e. I'm happy to assume the interpretation you give). What I'm not clear on (and what the local debate where I work has been) is where it's stated that the increments can both happen before one of the two calls is made. – Stuart Golodetz Jun 15 '12 at 08:45
2

No, per 6.5.2.2 10 there is no sequence point between the evaluation of subexpression arguments, just before the actual call.

One way of looking at it is that it is unspecified whether the behaviour is undefined; if the implementation sequences the two ++i subexpressions before any call to g or h then the behaviour is undefined, but if the ++i subexpressions are evaluated as late as possible (immediately before calling g and h respectively) then the behaviour is unspecified. However, because the implementation is always at liberty to choose between any allowed unspecified behaviour then the overall result is undefined.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Right - so it's possible for the two `++i`s to both happen before `g` and `h` are called, in which case there's no sequence point in the way and it would be undefined. – Stuart Golodetz Jun 12 '12 at 15:48
  • 5
    *unspecified whether the behaviour is undefined*: I think there is no such thing in C. The behavior is undefined here. – ouah Jun 12 '12 at 15:49
  • I think despite the terminology, I'm going to give this the answer, as it actually helped my thinking about this more (I upvoted both answers though as they were both helpful). It would probably be helpful to future readers if the terminology could be tightened up though I guess. – Stuart Golodetz Jun 13 '12 at 08:30