18

Background

Just had a chat with a C guy today and we disagreed on the following:

int intgA[2] = { 1, 2 };
int intgB[2] = { 3, 5 };

int *intAPtr = intgA;
int *intBPtr = intgB;

So when we do:

*intAPtr++ = *intBPtr++;

My analysis

First:

intBPtr increments by one, now pointing to the address of 5. Then, deference, holding the value 5;

intAPtr also increments by one, now pointing to the address of 2. Subsequently referencing and the value is 2;

Lastly:

2 is replaced by 5.

So respectively they are: 5 and 5.

His analysis

The value of *intBPtr is first assigned to *intAPtr first.

Hence, they become: 3 and 3.

Then both *intAPtr and *intBPtr is incremented by one.

So, respectively they become: 4 and 4.

My Assumption

I thought the ++ operator takes precedence both over * and =, hence my assertion.

For example if we had:

*intAPtr++; 

The result should be 2, right? Because we first increment the pointer and then dereference.

So why in the above case, as he claims, we first assign the value of intBPtr to the value of intAPtr and increment the values last?

After having taken all suggestions here, I ran the code in IDE and the result confirms that of @sujin:

Although it confirms that I was right at least in terms of precedence:

That: *intAPtr++ = *intBPtr++;

intAPtr++ has a higher precedence, which leads to: intAPtr increments its address by 1.

Now pointing to: the address of 2.

And likewise:

intBPtr++ also increments by 1 (address).

Now pointing to: the address of 5.

Then it's *'s turn:

So the both get dereferenced (*) to respectively 2 and 5.

But the problem exists still because the assignment above (=) did not seem to take place.

If it did both would become 5.

Looking forward to being further enlightened.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Unheilig
  • 16,196
  • 193
  • 68
  • 98
  • `intAPtr++` is a post-increment. It's highly likely that the increment operations occur *last*, after the assignment. Have you tried putting this code into an IDE and running it, to see what it does? – Robert Harvey Jan 06 '14 at 18:48
  • 1
    It is irrelevant when the increment of the pointer takes place in relation to the assignment. Post-increment's return value is a new temporary object, separate from the pointer that's being incremented. – Cubbi Jan 06 '14 at 18:50
  • @Cubbi: It would take 5 minutes to verify the behavior by simply running the code. – Robert Harvey Jan 06 '14 at 18:50

6 Answers6

9

The statement

*intAPtr++ = *intBPtr++;

is parsed as

*(intAPtr++) = *(intBPtr++);

and breaks down as follows:

  • The value currently pointed to by intBPtr (3) is assigned to the location pointed to by intAPtr (intgA[0]);
  • The pointers intAPtr and intBPtr are incremented.

The exact order in which these things happen is unspecified; you cannot rely on intBPtr being incremented after intAPtr or vice-versa, nor can you rely on the assignment occuring before the increments, etc.

So by the time this is all done, intgA[0] == 3 and intAPtr == &intgA[1] and intBPtr == &intgB[1].

The expression a++ evaluates to the value of a before the increment.

John Bode
  • 119,563
  • 19
  • 122
  • 198
5

You and the other guy both are wrong!

Either,
1. both pointers increment first and then assignment takes place or
2. one pointer increments, then the assignment happens, then the other pointer increments or
3. first assignment takes place and then pointers increments.

But the rule is that, all the side effects of one statement have to be complete before the next statement starts. Keep in mind that the original values must be used. As long as the original value is used, the increment can happen at any time.

C-faq: 3.2:

It is not guaranteed that an increment or decrement is performed immediately after giving up the previous value and before any other part of the expression is evaluated. It is merely guaranteed that the update will be performed sometime before the expression is considered "finished".

Read this answer given by Eric Lippert for detailed explanation.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 1
    Thanks for the shout-out. I note that you left a case out of your second statement. "... or one pointer increments, then the assignment happens, then the other pointer increments". – Eric Lippert Jan 07 '14 at 14:57
  • @EricLippert; Thanks for pointing it out. Added that case in the answer. :) – haccks Jan 07 '14 at 16:03
4

Yes, ++ binds tighter than *, but you have misunderstood how it works. var++ increments var, but it evaluates to the value of var before the increment. You can think of it as syntactic sugar for (var += 1, var - 1) if you like.

Another way to think about this is: if it worked the way you thought it did, then there would be no difference between var++ and ++var, and one of them would not have been included in the language in the first place. C has almost no redundancies.

(The binding precedence does matter; for instance, it means *var++ increments the value of the variable var whereas (*var)++ increments the value in the memory location at *var.)

zwol
  • 135,547
  • 38
  • 252
  • 361
  • 1
    The coworker isn't correct as well, because the `++` relates to the pointers and not to the values, as he seems to claim... – glglgl Jan 06 '14 at 18:53
  • @glglgl Doh, I misread the question. I think I'll just delete that statement rather than try to explain in more detail. – zwol Jan 06 '14 at 18:54
  • @haccks No. `var` is not necessarily a memory location (it could exist only in a register), but `*var` definitely is a memory location. I see that what I wrote could be clearer, though. – zwol Jan 06 '14 at 20:47
  • `var` is declared as a pointer. I think in the case of `*var++` , `var++` takes place first (precedence), which increments the memory location and then `*` takes place, which dereferences it to its value. If `var` were declared as a variable, `var++` would of course mean incrementing the value of `var`, as you claimed, which is not the case here because `var` is declared as a pointer. – Unheilig Jan 07 '14 at 16:08
  • @Unheilig No, you are clinging to your original misconception. The `++` in `*var++` *increments* `var` (and not the memory location `*var`) because of precedence, but it *evaluates to* the value of `var` **before the increment happened**, which means the value of the overall expression is the value in the memory location *originally pointed to* by `var`. Look again at what I wrote about syntactic sugar. – zwol Jan 07 '14 at 17:29
  • @Unheilig Also, `var` being a pointer does not mean that `var` is not a variable. In your original code, `intAPtr` and `intBPtr` are most definitely both variables and pointers. – zwol Jan 07 '14 at 17:30
3

The main effect of the ++ operator is to produce the value of its operand (without having incremented it). The side effect is to increment the operand.

Even if the side effect is performed before the value produced by the main effect is used in another operation, the value produced by the main effect is the original value of the operand, not the updated value.

In *x++, which is parsed as *(x++), the * operator is applied to the value produced by the main effect, so the * operation is the same as if *x is evaluated. The increment happens “on the side”, outside of the main expression evaluation, so it takes no part in determining the value of *x++.

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

Example code (In linux):

#include <stdio.h>
#include <stdlib.h>
int main() {
    int intgA[2] = { 1, 2 };
    int intgB[2] = { 3, 5 };
    int *intAPtr = intgA; //1
    int *intBPtr = intgB; //3
    *intAPtr++ = *intBPtr++;
    // *intAPtr = *intBPtr;
    // *intAPtr++ = 25; 
    printf("op: %d %d\n", *intAPtr, *intBPtr);

   return 0;
}

output:

op: 2 5

first its assigning intBPtr to intAPtr then increment take place, Because it is post increment.

sujin
  • 2,813
  • 2
  • 21
  • 33
  • 1
    *irst its assigning intBPtr to intAPtr then increment take place.*: how could you say that? – haccks Jan 06 '14 at 19:15
1

I agree with John Bode answer to simplify it a bit I will just write it in code:

   *intAptr = * intBPtr;

is equal to:

   *intAptr =*intBPtr 
   intAPtr+=1;
   inrBPtr+=1;

so what we get is: intAPtr points to 2 intBPtr points to 5

Coldsteel48
  • 3,482
  • 4
  • 26
  • 43