Post-increment on a dereferenced pointer?

48,865

Solution 1

Due to operator precedence rules and the fact that ++ is a postfix operator, add_one_v2() does dereference the pointer, but the ++ is actually being applied to the pointer itself. However, remember that C always uses pass-by-value: add_one_v2() is incrementing its local copy of the pointer, which will have no effect whatsoever on the value stored at that address.

As a test, replace add_one_v2() with these bits of code and see how the output is affected:

void add_one_v2(int *our_var_ptr)
{
    (*our_var_ptr)++;  // Now stores 64
}

void add_one_v2(int *our_var_ptr)
{
    *(our_var_ptr++);  // Increments the pointer, but this is a local
                       // copy of the pointer, so it doesn't do anything.
}

Solution 2

This is one of those little gotcha's that make C and C++ so much fun. If you want to bend your brain, figure out this one:

while (*dst++ = *src++) ;

It's a string copy. The pointers keep getting incremented until a character with a value of zero is copied. Once you know why this trick works, you'll never forget how ++ works on pointers again.

P.S. You can always override the operator order with parentheses. The following will increment the value pointed at, rather than the pointer itself:

(*our_var_ptr)++;

Solution 3

OK,

*our_var_ptr++;

it works like this:

  1. The dereference happens first, giving you the memory location indicated by our_var_ptr (which contains 63).
  2. Then the expression is evaluated, the result of 63 is still 63.
  3. The result is thrown away (you aren't doing anything with it).
  4. our_var_ptr is then incremented AFTER the evaluation. It's changing where the pointer is pointing to, not what it's pointing at.

It is effectively the same as doing this:

*our_var_ptr;
our_var_ptr = our_var_ptr + 1; 

Make sense? Mark Ransom's answer has a good example of this, except he actually uses the result.

Solution 4

Much confusion here, so here is a modified test program to make what happens clear (or at least clearer):

#include <stdio.h>

void add_one_v1(int *p){
  printf("v1: pre:   p = %p\n",p);
  printf("v1: pre:  *p = %d\n",*p);
    *p = *p + 1;
  printf("v1: post:  p = %p\n",p);
  printf("v1: post: *p = %d\n",*p);
}

void add_one_v2(int *p)
{
  printf("v2: pre:   p = %p\n",p);
  printf("v2: pre:  *p = %d\n",*p);
    int q = *p++;
  printf("v2: post:   p = %p\n",p);
  printf("v2: post:  *p = %d\n",*p);
  printf("v2: post:   q = %d\n",q);
}

int main()
{
  int ary[2] = {63, -63};
  int *ptr = ary;

    add_one_v1(ptr);         
    printf("@ %p\n", ptr);
    printf("%d\n", *(ptr));  
    printf("%d\n\n", *(ptr+1)); 

    add_one_v2(ptr);
    printf("@ %p\n", ptr);
    printf("%d\n", *ptr);
    printf("%d\n", *(ptr+1)); 
}

with the resulting output:

v1: pre:   p = 0xbfffecb4
v1: pre:  *p = 63
v1: post:  p = 0xbfffecb4
v1: post: *p = 64
@ 0xbfffecb4
64
-63

v2: pre:   p = 0xbfffecb4
v2: pre:  *p = 64
v2: post:  p = 0xbfffecb8
v2: post: *p = -63
v2: post:  q = 64

@ 0xbfffecb4
64
-63

Four things to note:

  1. changes to the local copy of the pointer are not reflected in the calling pointer.
  2. changes to the target of the local pointer do affect the target of the calling pointer (at least until the target pointer is updated)
  3. the value pointed to in add_one_v2 is not incremented and neither is the following value, but the pointer is
  4. the increment of the pointer in add_one_v2 happens after the dereference

Why?

  • Because ++ binds more tightly than * (as dereference or multiplication) so the increment in add_one_v2 applies to the pointer, and not what it points at.
  • post increments happen after the evaluation of the term, so the dereference gets the first value in the array (element 0).

Solution 5

As the others have pointed out, operator precedence cause the expression in the v2 function to be seen as *(our_var_ptr++).

However, since this is a post-increment operator, it's not quite true to say that it increments the pointer and then dereferences it. If this were true I don't think you'd be getting 63 as your output, since it would be returning the value in the next memory location. Actually, I believe the logical sequence of operations is:

  1. Save off the current value of the pointer
  2. Increment the pointer
  3. Dereference the pointer value saved in step 1

As htw explained, you aren't seeing the change to the value of the pointer because it is being passed by value to the function.

Share:
48,865
ChristopheD
Author by

ChristopheD

I'm an experienced "can-do" attitude Software Developer from Belgium (currently working in Eastern Canada), skilled and interested in a variety of technology stacks: C#, Python (Gold SO badge), Node.JS, Perl, ... (and many more) I mostly enjoy coming up with elegant solutions to difficult problems.

Updated on July 05, 2022

Comments

  • ChristopheD
    ChristopheD almost 2 years

    Trying to understand the behaviour of pointers in C, I was a little surprised by the following (example code below):

    #include <stdio.h>
    
    void add_one_v1(int *our_var_ptr)
    {
        *our_var_ptr = *our_var_ptr +1;
    }
    
    void add_one_v2(int *our_var_ptr)
    {
        *our_var_ptr++;
    }
    
    int main()
    {
        int testvar;
    
        testvar = 63;
        add_one_v1(&(testvar));         /* Try first version of the function */
        printf("%d\n", testvar);        /* Prints out 64                     */
        printf("@ %p\n\n", &(testvar));
    
        testvar = 63;
        add_one_v2(&(testvar));         /* Try first version of the function */
        printf("%d\n", testvar);        /* Prints 63 ?                       */
        printf("@ %p\n", &(testvar));   /* Address remains identical         */
    }
    

    Output:

    64
    @ 0xbf84c6b0
    
    63
    @ 0xbf84c6b0
    

    What exactly does the *our_var_ptr++ statement in the second function (add_one_v2) do since it's clearly not the same as *our_var_ptr = *our_var_ptr +1?

  • Torlack
    Torlack almost 15 years
    That isn't true about the order of operation. In add_one_v2, the ++ is applied to the pointer, not the dereference. However, since it is a post increment, the dereference is happening BEFORE the increment.
  • hbw
    hbw almost 15 years
    Are you talking about the original add_one_v2, or one of my examples with parentheses?
  • Torlack
    Torlack almost 15 years
    I'm talking about the original. I'm just trying to point out that your statement of "is incrementing the pointer and then dereferencing..." is incorrect.
  • ChristopheD
    ChristopheD almost 15 years
    Thanks for the quick answer (and for the informative comment Torlack!). ++*our_var_ptr seems to 'work' the same as add_one_v1. One thing i don't understand though: if the functions are only working with local copies of the pointers --> how do they achieve to influence the value printed in main? Or do i misinterpret that statement?
  • Torlack
    Torlack almost 15 years
    The pointer itself is a local copy, but the value it is pointing at isn't.
  • hbw
    hbw almost 15 years
    @Torlack: Gotcha, thanks for the reminder. Fixed. @ChristopheD: If you dereference a pointer, you can change the value stored in that location in memory. So, basically, you have a copy of the memory address of the value—you can't influence what memory address the pointer will hold in main, but you can change what value that memory address points to.
  • ChristopheD
    ChristopheD almost 15 years
    Thanks a lot (htw, torlack): that makes sense!
  • ChristopheD
    ChristopheD almost 15 years
    Thanks for answering, i think the advice about making it more explicit by using brackets is solid!
  • ChristopheD
    ChristopheD almost 15 years
    Thanks for taking the time for writing this answer, it's this kind of stuff that makes StackOverflow so great an environment! I've had more insightful answers here in one hour than i would have found in the same time in my lousy C tutorials ;)
  • Dan
    Dan over 11 years
    Doesn't actually answer the question. He was asking what *ptr++ does. Correct answer involves describing precedence.
  • Benjamin Barenblat
    Benjamin Barenblat over 10 years
    This example, along with quite a few other “Look ma, no hands!” code snippets, appears in that bastion of C knowledge, The C Programming Language.
  • kumar
    kumar almost 10 years
    Thanks! I think this is the most important point - "Because ++ binds more tightly than *"
  • kumar
    kumar almost 10 years
    Also, folks should make note of this : int q = *p++; IS NOT EQUIVALENT TO *p++; *p++ will still have "first value" only. whereas in q it is "second value".( I think you mentioned it in second point)
  • a.saurabh
    a.saurabh over 9 years
    GCC doesn't accept: *our_var_ptr++; It accepts as: *(our_var_ptr)++
  • a.saurabh
    a.saurabh over 9 years
    Also IMO it should be *our_var_ptr; *our_var_ptr = *our_var_ptr + 1;
  • BIBD
    BIBD about 9 years
    @a.saurabh I guarantee GCC will accept *our_var_ptr++. It might throw a warning, but if you took that away, you'd break SOOOO much stuff. With *(our_var_ptr)++ the inside of the parenthesis is evaluated first to our_var_ptr; which equals *our_var_ptr++.
  • BIBD
    BIBD about 9 years
    @a.saurabh *our_var_ptr = *our_var_ptr + 1; is the effect the original poster wanted; not what I was trying to explain. our_var_ptr = our_var_ptr + 1; is the reason why this while (*dst++ = *src++) ; works as a string copy in C.
  • Shir
    Shir over 5 years
    char *x = {'a', 'c'}; char y = *++x; //or *(++x); char z = *x; y and z will be a or c?
  • Jango
    Jango over 5 years
    @Shir with 'c'. Thank you for pointing that! I fixed it in the answer.
  • orion elenzil
    orion elenzil almost 2 years
    imo, this is bonkers. coming to the language with fresh eyes (I'm not), I would very much expect the incremented value to be dereferenced. Thanks for spelling out the situation here.