Why postfix increment is conducted at last in a condition evaluation?

Viewed 155

GDB shows the obj->start_point is incremented after the whole evaluation >=.

But C Primer Plus told me ++ has the higher precedence than >=.

So why the obj->start_point doesn't increment first? Is the book wrong?

if (obj->start_point++ >= obj->data + obj->data_size) {
3 Answers

Precedence only controls the grouping of operators with operands, not the order in which expressions are evaluated.

The side effect of the ++ operator does not have to be applied immediately after evaluation, it only needs to be applied before the next sequence point.

Edit

You can certainly look at the generated assembly code to see how your compiler handled that particular case, just be aware that depends on the code and how you compiled it (different levels of optimization will likely affect it).

With a few exceptions1, C does not require expressions to be evaluated in any particular order (left to right, right to left, or any other order). In this particular case, all that matters is that we’re comparing the result of obj->data + obj->data_size to the result of obj->start_point++. Any of the following orders of evaluation are possible:

Order 1        
–––––––
tmp1 = obj->data + obj->data_size
result = obj->start_point >= tmp1
obj->start_point = obj->start_point + 1

Order 2
–––––––
tmp1 = obj->start_point
obj->start_point = obj->start_point + 1
tmp2 = obj->data + obj->data_size
result = tmp1 >= tmp2

Order 3
–––––—–
tmp1 = obj->start_point
tmp2 = obj->data + obj->data_size
obj->start_point = obj->start_point + 1
result = tmp1 >= tmp2

Or something else completely.

The evaluations of obj->start_point++ and obj->data + obj->data_size can even be interleaved with each other - after all, each of obj->start_point, obj->data, and obj->data_size are expressions that need to be evaluated as well.

As long as the result is correct - we are comparing the result of the postfix ++ operator on obj->start_point to the result of the addition of obj->data and obj->data_size - the compiler has a lot of freedom in how it orders the evaluation of each subexpression, and that order does not have to be consistent over the entire program (a similar expression elsewhere in the same program may be evaluated differently).

Answering leoleohu's question from the comments

leoleohu asks

But my question is still there, who decides postfix ++ is conducted after >=, and prefix ++ is conducted before >=?

It's up to the compiler, and it doesn't have to be the same for every expression in the same code.

The ++ and -- operators, both prefix and postfix forms, have a result and a side effect.

The result of obj->start_point++ is the current value of obj->start_point. The side effect is to add 1 to obj->start_point. In the expression

obj->start_point++ >= obj->data + obj->data_size

the result of obj->start_point++ (the current value of obj->start_point) is compared to the result of obj->data + obj->data_size.

The side effect of adding 1 to obj->start_point can happen at any point after evaluation, as long as it occurs before the next sequence point. It can be applied before or after the comparison - the compiler is free to make that decision based on considerations like current level of optimization, other operands in the expression, surrounding code, etc.


  1. The &&, ||, ?:, and comma operator (which is not the same thing as the comma that separates arguments in a function call) all force left-to-right evaluation.

It does have a higher precedence. The issue is that variable++ returns the value before the increment. If you want to return the new value you have to prefix it: ++variable.

In your case: if (++obj->start_point >= obj->data + obj->data_size) {

Doing variable++ is equivalent to call this function:

int increment_posfix(int *x)
{
    int temp = *x;
    *x = *x + 1;
    return temp;
}

While doing ++variable is equivalent to:

int increment_prefix(int *x)
{
    *x = *x + 1;
    return *x;
}

When you ask "Why doesn't it increment first?", you have to be very careful about what you're asking. When you say obj->start_point++, there are at least three separate things that are going to happen:

  1. At some point, we are going to take the old value of obj->start_point and compute a new value, obj->start_point + 1.
  2. At some point, we are going to take that new value and store it back into obj->start_point.
  3. We are going to "return" some value to the outer expression. In this case, we're going to use the >= operator to compare that "returned" value to the subexpression obj->data + obj->data_size.

Now, there are only two statements we can make with any certainty, which I'll call "A" and "B".

Statement "A" -- and this is the most important one -- is the answer to question #3. For the postfix form of ++, the value that is "returned" to the outer expression is the old value. This is the very definition of the postfix ++ operation. This is not an "order of operation" question, it's a "what does the operator do?" question.

And then statement "B", which is less interesting, and which is so obvious that you might not even think of it as a "thing", is that #1 has to happen sometime before #2 -- we can't store the new value until after we have computed it.

But then it's also super important -- this might even be worth labeling as "statement C" -- to realize that we cannot say anything else. We absolutely do not know (in any absolute sense) when thing #1 will happen. We also do not know that thing #2 will happen. All we know is that thing #2 will happen after thing #1, and sometime before the whole expression is finished (that is, formally, before the next sequence point). (So maybe there's four things we can say.)

See also this older question, Why does a=(b++) have the same behavior as a=b++?

Related