Search code examples
cconditional-operatorunary-operatorsequence-pointspostfix-operator

Will this expression evaluate to true or false (1 or 0) in C?


#include<stdio.h>
int main()
{
    int a=4;
    int b=4;
    int c= a++ < ++b? 1 : 0;
    
    printf ("%d",c);
}

It is known that there is a sequence point at ?, which means that both the prefix and postfix operations have to be completed by that point. Also it is known(?) that b is incremented before the comparison. However, is a incremented before or after the comparison?

If it is incremented before the < test, then the Boolean evaluates to false and c is set to 0, else to true with c being set to 1. In my compiler, it evaluates to true, which means a++ is performed after the comparison operation with c being set to 1.

Is this behavior part of the specification though?

I modified it to

#include<stdio.h>
int main()
{
    int a=4;
    int b=4;
    int d=2;
    int c= a++ + d < ++b + d? 1 : 0;
    
    printf ("%d",c);
}

and it still evaluates to 1. The postfix has to complete before the ?, but does that really ensure that it happens after the comparison < ?


Solution

  • Also it is known(?) that b is incremented before the comparison. However, is a incremented before or after the comparison?

    This is a subtle point, but it's important to understand what's really going on here.

    Both the subexpressions a++ and ++b do two things. They compute a new value to be used in the surrounding expression, and they update the stored value of the variable they're operating on.

    So a++ does this:

    1. it yields the old value of a (4) out to the surrounding expression
    2. it stores a new value (5) into a.

    And ++b does this:

    1. it yields the new value of b (4+1 or 5) out to the surrounding expression
    2. it stores a new value (5) into b.

    Notice that in both cases it's thing 1 that the < operator cares about. And, in both cases, thing 1 is an absolute definition, it doesn't depend on timing.

    Or, in other words, asking "Is a/b incremented before or after the comparison?" is not really the right question. The values a and b+1 participate in the comparison, and that's it.

    Where the timing comes in is things 2. We don't know, precisely, when the new value gets stored back into a. Nor do we know precisely when the new value gets stored back into b. All we know is that those stores will happen sometime before the next sequence point (which, as you correctly note, in this case is the ? part of the ternary operator).

    But nothing depends on those timings, so there's no undefined behavior here.

    Undefined behavior comes in when either

    1. the variable that's modified (a or b) also has its value independently used elsewhere in the expression, meaning that we don't know whether that use uses the old or the new value
    2. the same variable is modified twice, meaning that we don't know which of the two modifications "wins"

    But, again, neither of those problems occurs here, so the expression in the question is well-defined.

    What would not be defined would be if you said something like

    c = a++ < ++a ? 1 : 0;           /* WRONG */
    

    This expression is rampantly, classically undefined, because now we have two side effects affecting the same object, a. Now, when we try to say that "a++ yields the old value of a out to the surrounding expression", we have a major, significant, unresolvable ambiguity: does "the old value of a" mean the value a had before this expression ever began, or could it mean the value of a as modified by some other part of the the expression we're in the middle of?

    And there is no, repeat no, way to answer this question. It's tempting to read an expression left to right, or to look at the defined associativity of various operators, but there's no rule in C to say that those factors actually influence the order that side effects happen in. So the expression a++ < ++a is undefined, even though the similar-looking a++ < ++b is well-defined.