1


I have the following code which produces unexpected results to me:

 #include < stdio.h >

int a = 0, value;

int main(void)
{
    // Testing the evaluation order of multiple
    // conditional operators:
    value = (a == 3) ? 3 : (a = 3) ? 5 : 0;
    printf("%d\n", value);

    return 0;
}

I was expecting for this code to print 3, seeing that the conditional operator evaluates
from right to left and that there is a sequence point at the ? of the first-to-be executed
operation, whereas it actually prints 5.
Is it wrong to assume that side effects of an expression residing between two sequence
points also get calculated when the values of the expressions are?
If i add printf("%d\n" a); i get 3 printed though, so the side effect gets done.
Or is it just that control dosent really pass to the subexpression the value of which
is being calculated "first" officially?
I would rather bet on the latter because changing the value of 'a' to 3 and the rvalue
in the assignment of the second conditional to 4 resulted in short-circuit evaluation
of the first conditional expression, meaning that i got 3 printed for both 'a' and 'value'.
I got the above result on Lubuntu 14.04 with GCC 4.8.2 using the -std=c99 flag.
Thank you for anyone clearing me up on this matter!

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
Larry
  • 427
  • 2
  • 10

3 Answers3

6

The conditional operator does not "evaluate right to left". The standard (C11 6.5.15/4) says:

The first operand is evaluated; there is a sequence point between its evaluation and the evaluation of the second or third operand (whichever is evaluated). The second operand is evaluated only if the first compares unequal to 0; the third operand is evaluated only if the first compares equal to 0; the result is the value of the second or third operand (whichever is evaluated)

So the expression (a == 3) ? 3 : (a = 3) ? 5 : 0; evaluates in these steps:

  • (a == 3) result is 0
  • (a = 3) result is 3, unequal to 0
  • 5

So 5 is what is assigned to value.

You might be confusing the concept of how the conditional operator is evaluated with how the conditional operator associates (or groups). The syntax of C specifies that the expression:

(a == 3) ? 3 : (a = 3) ? 5 : 0;

associates or groups sub expressions like so:

((a == 3) ? 3 : ((a = 3) ? 5 : 0));

which is often described as 'associates right'. However, this grouping/associativity doesn't affect the fact that the expression is still evaluated left-to-right and that the second conditional expression only evaluates after the first operand in the 'outer' conditional expression is evaluated.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • I see. So the associativity dosen't determine which operation will execute first. It is hard to make certain of unless with a complex expression that has a side effect and is sequenced in lucky positions. Is every expression evaluated left to right? Or is the order implementation defined except for the sequencing rule imposed by the standard? But still, an operator has to get a value somewhere, so if lets say its precedence would be lower than that of another one, it would *have* to be evaluated first to provide a value to the other, right? – Larry Sep 15 '14 at 21:28
  • The standard says "Except as specified later, side effects and value computations of subexpressions are unsequenced". As you say, if the result of a sub-expression is used in a larger part of the expression, then it have to be evaluated first. However, it's not always obvious how far ahead of time the evaluation might occur, and for C there is no general requirement that expressions have to be evaluated left-to-right. There is also the issue about when side-effects take place, and those are governed by the sequence point rules. – Michael Burr Sep 15 '14 at 21:34
  • Holy cow! Well, i knew side effects had to be used carefully, but it seems getting values from more complex expressions is a downright mess! Ill read what the standard draft sais about evaluation, but according to my memories, most of it is written in such a cryptic way that i just cant understand. Also, because its using terms i havent learnd about yet. Thank you very much for your answer, if i ever get to 15 rep points, ill upvote it! – Larry Sep 16 '14 at 03:58
  • In my opinion, it's best to keep side effects mostly to themselves. The days where the compiler needed help to optimize things by packing side effects into larger expressions are for the most part over. There are times when it might be idiomatic (like `*dst++ = *src++`) or where syntax might force you to cram more than one side effect into a single expression (like controlling clauses of a `for` loop), but for the most part there's really not much need. I think multiple side-effect expressions usually make for more difficult to understand code as well. – Michael Burr Sep 16 '14 at 04:11
  • I am sorry to bother you again, but it occured to me that i basically didnt have a clue as to what caused the result unexpected to me, so my question is pretty irrelevant. I thought that it would be much more useful to have this question under a title like: "order of evaluation vs expression grouping" or something similar. Is it possible to close it as nonconstructive or the like and reopen it with another title? As far as i went searching for it to learn more on this topic, i couldnt find a thread that discusses this. – Larry Sep 23 '14 at 20:09
  • @Larry: I'm not sure exactly what you're looking for, but you might want to read the question and answers here: http://stackoverflow.com/questions/5473107/operator-precedence-vs-order-of-evaluation there's not much about the ternary operator specifically, but there's a lot of information about operator precedence, associativity, order of evaluation, sequence points and undefined behavior. One key thing is that the conditional operator is one of the few that *does* impose a sequence point, so you can be sure that the left side of the expression will be evaluated before the right side. – Michael Burr Sep 23 '14 at 20:37
  • My gosh, this is EXACTLY what i was looking for. Very strange that i didnt find it even through typing its exact title into google. My problem is just that i made a mistake by thinking that my case was specific to the ternary operator or gcc. Thank you again! As for my post, i dont mind if it gets deleted, the title is totally confusing. – Larry Sep 23 '14 at 20:51
0

Let's trace through this one part at a time. You have this expression:

value = (a == 3) ? 3 : (a = 3) ? 5 : 0;

Since a starts off at 0, we skip the 3 branch of the first ?: and look at the second branch, which is

(a = 3) ? 5 : 0

The condition here is a = 3, which sets a to 3 and then evaluates to the new value of a, which is 3. Since 3 is nonzero, we take the first branch of the ?:, so the expression evaluates to 5. The net effect is that a is set to 3 and value is set to 5.

The language spec guarantees that the evaluation order is indeed what you think it should be - the "if" and "else" branches of the ?: operator are guaranteed not to execute unless the condition works out as it does, so there are sequence points here. I think you just misunderstood the effect of the a = 3 operation.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • I think the *order of evaluation* vs *grouping* was the key thing here. I dont think i misunderstood the effect of a = 3 here, but i could be wrong since im at the very beginning of learning how to program. I expected the operation to return true and store the value *3* in the *a* variable *before* the first conditional operator takes effect. – Larry Sep 16 '14 at 04:01
  • I mean *a* evaluating as true as in nonzero of course, not as the value *true*. a = 3 should return 3... – Larry Sep 16 '14 at 04:14
0

The conditional operator evaluates left-to-right (evaluating the condition before either of the branches). You may be confusing this with its right-associativity (in which it appears to bind right-to-left).

Your conditional expression essentially results in the following logic:

if(a == 3) {
    value = 3;
} else {
    if(a = 3) {
        value = 5;
    } else {
        value = 0;
    }
}

Note that the conditional doesn't execute a branch until after the condition is evaluated.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • Thanks, ive seen this setup already, but didnt take the order of execution of the parts literally. – Larry Sep 16 '14 at 04:02