Post-increment on a dereferenced pointer?
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
?
Solution 1:
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 2:
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 3:
OK,
*our_var_ptr++;
it works like this:
- The dereference happens first, giving you the memory location indicated by
our_var_ptr
(which contains 63). - Then the expression is evaluated, the result of 63 is still 63.
- The result is thrown away (you aren't doing anything with it).
-
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:
- changes to the local copy of the pointer are not reflected in the calling pointer.
- changes to the target of the local pointer do affect the target of the calling pointer (at least until the target pointer is updated)
- the value pointed to in
add_one_v2
is not incremented and neither is the following value, but the pointer is - 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 inadd_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:
- Save off the current value of the pointer
- Increment the pointer
- 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.