2

I just wonder if, for the following code, the compiler uses associativity/precedence alone or some other logic to evaluate.

int i = 0, k = 0;

i = k++;

If we evaluate based on associativity and precedence, postfix ++ has higher precedence than =, so k++(which becomes 1) is evaluated first and then comes =, now the value of k which is 1 is assigned to i.

So the value of i and k would be 1. However, the value of i is 0 and k is 1.

So I think that the compiler splits this i = k++; into two (i = k; k++;). So here compiler is not going for the statements associativity/precedence, it splits the line as well. Can someone explain how the compiler resolves these kinds of statements?

Priyanshul Govil
  • 518
  • 5
  • 20
Franc
  • 319
  • 9
  • 28
  • " so k++(which becomes 1)" --> Not quite. `k` becomes 1, yet its prior value of 0 is "returned". – chux - Reinstate Monica Feb 01 '21 at 10:32
  • _"So I think compiler splits this `i = k++;` into two `i = k ; k++;`"_. Did you try it? – Jabberwocky Feb 01 '21 at 10:32
  • 4
    It does not really need to be explained at the compiler level. The behaviour is mandated by the C language specification. From the spec: *The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented* and then the section on order of precedence specifies `++` comes before `=` as you have noted. – kaylum Feb 01 '21 at 10:33
  • 1
    It has nothing to do with precedence. I has to do with what value the operators returns. – klutt Feb 01 '21 at 10:36
  • @kaylum, could you tell me the link to spec? Is that C11 (ISO/IEC 9899:2011) ? – Franc Feb 01 '21 at 10:43
  • `could you tell me the link to spec?` [Here you go - a working draft hosted on port70.net/6.5.2.4p2](https://port70.net/~nsz/c/c11/n1570.html#6.5.2.4p2). – KamilCuk Feb 01 '21 at 11:18
  • Teachers are often hellbent on teaching operator precedence but they rarely explain order of evaluation for some reason. See [What is the difference between operator precedence and order of evaluation?](https://software.codidact.com/posts/278172) for an explanation of the difference. – Lundin Feb 01 '21 at 11:53

8 Answers8

2

++ does two separate things.

k++ does two things:

  • It has the value of k before any increment is performed.
  • It increments k.

These are separate:

  • Producing the value of k occurs as part of the main evaluation of i = k++;.
  • Incrementing k is a side effect. It is not part of the main evaluation. The program may increment the value of k after evaluating the rest of the expression or during it. It may even increment the value before the rest of the expression, as long as it “remembers” the pre-increment value to use for the expression.

Precedence and associativity are not involved.

This effectively has nothing to do with precedence or associativity. The increment part of a ++ operator is always separate from the main evaluation of an expression. The value used for k++ is always the value of k before the increment regardless of what other operators are present.

Supplement

It is important to understand that the increment part of ++ is detached from the main evaluation and is sort of “floating around” in time–it is not anchored to a certain spot in the code, and you do not control when it occurs. This is important because if there is another use or modification of the operand, such as in k * k++, the increment can occur before, during, or after the main evaluation of the other occurrence. When this happens, the C standard does not define the behavior of the program.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

Postfix operators have higher precedence than assignment operators.

This expression with the assignment operator

i = k++

contains two operands.

It is equivalently can be rewritten like

i = ( k++ );

The value of the expression k++ is 0. So the variable i will get the value 0.

The operands of the assignment operator can be evaluated in any order.

According to the C Standard (6.5.2.4 Postfix increment and decrement operators)

2 The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it).

And (6.5.16 Assignment operators)

3 An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment,111) but is not an lvalue. The type of an assignment expression is the type the left operand would have after lvalue conversion. The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    This post never gets around to answering the question; it never explains the increment is a separate action from the main evaluation. It does quote the standard but does not explain it or state why the quote is relevant, and only after unhelpful text about precedence and rewriting the expression. (OP is not confused about the fact that the `++` is applied to `k`, which would be affected by precedence, but about the fact that the increment does not affect the value used for `k`, which is a feature of the operator, not of precedence.) Then this answer goes on to quote an irrelevant passage. – Eric Postpischil Feb 01 '21 at 11:36
0

It's similar to (only with an additional sequence point for illustration):

i = k; // i = 0
k = k + 1;  // k = 1
Devolus
  • 21,661
  • 13
  • 66
  • 113
  • 1
    This is not the same because it has a sequence point between the assignment and the increment that does not exist in the original code. Stack Overflow sees numerous repetitions of questions about `++` operators that arise because students do not understand the importance and effects of sequencing, so it is important to teach it correctly. – Eric Postpischil Feb 01 '21 at 11:01
0

Unlike C++, C does not have "pass by reference". Only "pass by value". I'm going to borrow some C++ to explain. Let's implement the functionality of ++ for both postfix and prefix as regular functions:

// Same as ++x
int inc_prefix(int &x) { // & is for pass by reference
    x += 1;
    return x;
}

// Same as x++
int inc_postfix(int &x) {
    int tmp = x;
    x += 1;
    return tmp;
}

So your code is now equivalent to:

i = inc_postfix(k);

EDIT:

It's not completely equivalent for more complex things. Function calls introduces sequence points for instance. But the above is enough to explain what happens for OP.

klutt
  • 30,332
  • 17
  • 55
  • 95
0

Operator associativity doesn't apply here. Operator precedence merely states which operand that sticks to which operator. It's not particularly relevant in this case, it just says that the expression should be parsed as i = (k++); and not as (i = k)++; which wouldn't make any sense.

From there on, how this expression is evaluated/executed is specified by specific rules for each operator. The postfix operator is specified to behave as (6.5.2.4):

The value computation of the result is sequenced before the side effect of updating the stored value of the operand.

That is, k++ is guaranteed to evaluate to 0 and then at some point later on, k is increased by 1. We don't really know when, only that it happens somewhere between the point when k++ is evaluated but before the next sequence point, in this case the ; at the end of the line.

The assignment operator behaves as (6.5.16):

The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands.

In this case, the right operand of = has its value computed before updating the left operand.

In practice, this means that the executable can look as either this:

  • k is evaluated to 0
  • set i to 0
  • increase k by 1
  • semicolon/sequence point

Or this:

  • k is evaluated to 0
  • increase k by 1
  • set i to 0
  • semicolon/sequence point
Lundin
  • 195,001
  • 40
  • 254
  • 396
0

The fundamental issue here is that precedence is not the right way to think about what

i = k=+;

means.

Let's talk about what k++ actually means. The definition of k++ is that if gives you the old value of k, and then adds 1 to the stored value of k. (Or, stated another way, it takes the old value of k, plus 1, and stores it back into k, while giving you the old value of k.)

As far as the rest of the expression is concerned, the important thing is what the value of k++ is. So when you say

i = k++;

the answer to the question of "What gets stored in i?" is, "The old value of k".

When we answer the question of "What gets stored in i?", we don't think about precedence at all. We think about the meaning of the postfix ++ operator.

See also this older question.

Postscript: The other thing you have to be really careful about is when you think about the side question, "When does it store the new value into k? It turns out that's a really hard question to answer, because the answer is not as well defined as you might like. The new value gets stored back into k sometime before the end of the larger expression it's in (formally, "before the next sequence point"), but we don't know whether it happens before or after, say, the point at which the thing gets stored into i, or before or after other interesting points in the expression.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
0

Precedence and associativity only affect how operators and operands are associated with each other - they do not affect the order in which expressions are evaluated. Precedence rules dictate that

i = k++

is parsed as

i = (k++)

instead of something like

(i = k)++

The postfix ++ operator has a result and a side effect. In the expression

i = k++

the result of k++ is the current value of k, which gets assigned to i. The side effect is to increment k.

It's logically equivalent to writing

tmp = k
i = tmp
k = k + 1

with the caveat that the assignment to i and the update to k can happen in any order - the operations can even be interleaved with each other. What matters is that i gets the value of k before the increment and that k gets incremented, not necessarily the order in which those operations occur.

John Bode
  • 119,563
  • 19
  • 122
  • 198
-1

Ahh, this is quite an interesting question. To help you understand better, this is what actually happens.

I'm going to try to explain using a bit of operator overloading concepts from C++, so bear with me if you do not know C++.

This is how you would overload the postfix-increment operator:

int operator++(int) // Note that the 'int' parameter is just a C++ way of saying that this is the postfix and not prefix operator
{
    int copy = *this;  // *this just means the current object which is calling the function
    *this += 1;
    return copy;
}

Essentially what the postfix-increment operator does is that it creates a copy of the operand, increases the original variable, and then returns the copy.

In your case of i = k++, k++ does actually happen first but the value returned is actually k (think of it like a function call). This then gets assigned to i.

Priyanshul Govil
  • 518
  • 5
  • 20
  • How do you expect someone who is asking a C question to understand your answer assuming he doesn't know C++? – Jabberwocky Feb 01 '21 at 12:37
  • @Jabberwocky I think I've explained how the process execution actually works. Plus its a very self-explanatory and intuitive piece of code. I don't think anyone should have a problem abstracting the details out of it. – Priyanshul Govil Feb 01 '21 at 12:40