2

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) {
Guodong Hu
  • 303
  • 1
  • 2
  • 11
  • Yes, that's the whole point of a postfix, first check/read variable, then increment it. Otherwise use ++obj->start_point – JeroSquartini Jan 24 '21 at 02:25
  • Why does it matter? From the perspective of the rest of your program, there is no difference. And the order of those two has absolutely nothing to do with operator association. – Jonathon Reinhart Jan 24 '21 at 02:26
  • If you want to increment first then just use ++ before variable... – Paul Bob Jan 24 '21 at 02:27
  • 1
    Post-increment having higher precedence than `>=` means that `a > b++` will be interpreted as `a > ( b++ )` rather than `( a > b )++`. It says nothing about when the increment will actually happen. – ikegami Jan 24 '21 at 02:37
  • See also [Why does `a=(b++)` have the same behavior as `a=b++`?](https://stackoverflow.com/questions/31087537/why-does-a-b-have-the-same-behavior-as-a-b). – Steve Summit Jan 25 '21 at 14:04

3 Answers3

4

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.
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • 1
    Thank you for the explanation. I can understand now, that grouping controls which value will be incremented after the evaluation. a > b++ means b will incremented, not the result of (a > b). And is it the compiler rules to decide when ++ will be applied? Will it be more clear if I read the assembly codes? – Guodong Hu Jan 24 '21 at 03:17
  • If C does not require expressions to be evaluated in any particular order, and compilers do that. Then what's the meaning of C programming language operation precedence: https://en.cppreference.com/w/c/language/operator_precedence I used to believe, with this precedence order table, we can make sure the ++ happens at first: at Order 4 –––––—– obj->start_point = obj->start_point + 1 tmp1 = obj->start_point tmp2 = obj->data + obj->data_size result = tmp1 >= tmp2 – Guodong Hu Jan 25 '21 at 01:56
  • @leoleohu: Precedence only controls the grouping of operators and operands - it only means that `obj->start_point++ >= obj->data + obj->data_size` will be parsed as `((obj->start_point)++) >= ((obj->data) + (obj->data_size))`. The operand of `++` is `obj->start_point`, the operands of `+` are `obj->data` and `obj->data_size`, etc. – John Bode Jan 25 '21 at 03:39
  • after reading the C++ Primer Plus and wiki, I know a concept called sequence point. And from wiki: "At the end of a full expression. This category includes expression statements (such as the assignment a=b;), return statements, the controlling expressions of if, switch, while, or do-while statements, and all three expressions in a for statement.", I know you are right, it only guarantees the "++" will be conducted before next sequence point. But my question is still there, who decides postfix ++ is conducted after >=, and prefix ++ is conducted before >=? Thanks a lot. – Guodong Hu Jan 25 '21 at 13:54
  • @leoleohu It's not correct to say that "postfix ++ is conducted after >=". It's better to say that "postfix ++ yields the old (unincremented) value to the surrounding expression". See also [my answer](https://stackoverflow.com/questions/65866601/why-postfix-increment-is-conducted-at-last-in-a-condition-evaluation/65886505#65886505). – Steve Summit Jan 25 '21 at 14:51
2

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;
}
vmp
  • 2,370
  • 1
  • 13
  • 17
1

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++?

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • Thank you Steve for the explanation of what actually happens of a postfix ++. And I learnt the difference of result and side effect, and concept of sequence point. It's the result of postfix that is used to compared with another expression. Thanks a lot to you and John Bode. – Guodong Hu Jan 26 '21 at 02:04