Pointer expressions: **ptr++, *++*ptr and ++**ptr use
I am trying my hands on a C pointer literature. In one of the illustrations, I encountered the following code.
# include <stdio.h>
int main()
{
static int a[]={0,1,2,3,4};
static int *p[]={a, a+1, a+2, a+3, a+4};
int **ptr;
ptr =p;
**ptr++;
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);
*++*ptr;
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);
++**ptr;
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);
return 0;
}
I receive the output as.
1 1 1
1 2 2
1 2 3
I am facing a problem in justifying this output. I made lot of boxes on a copy for easy grasp of the problem. I am able to justify the output 1 1 1
, my trouble starts with the statement, *++*ptr
.
Since, a unary operators are executed from right to left. So, *ptr
would be tackled first, then the value at ptr
would be incremented.
After this increment, I am not sure what happens, the book says that somehow p
is also incremented to point to the next element in this array. The output 1 2 2
can only be achieved through the increment of p
.
I am not sure that this kind of question fits exactly on stackoverflow.
I tried my best, wasted at least 10 pages with boxes drawn over them.
Any clarification would be appreciated.
Remember array name can easily decays into pointer to first element in most expressions (read some exceptions where array name not decaying into a pointer to first element? ably answered by @H2CO3).
For better understanding, consider my diagrams:
First, suppose a
stored in memory as follows.
a
+----+----+----+----+---+
| 0 | 1 | 2 | 3 | 4 |
+----+----+----+----+---+
▲ ▲ ▲ ▲ ▲
| | | | |
a a+1 a+2 a+3 a+3
Declaration static int *p[] = {a, a+1, a+2, a+3, a+4};
creates a new array of pointers to integer, with following values:
p[0] == a
p[1] == a + 1
p[2] == a + 2
p[3] == a + 3
p[4] == a + 4
Now, p
can also be assume to be stored in memory something like below:
p
+----+----+----+----+-----+
| a |a +1| a+2| a+3| a+4 |
+----+----+----+----+-----+
▲ ▲ ▲ ▲ ▲
| | | | |
p p+1 p+2 p+3 p+4
After assignment ptr = p;
things will be something like this:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a |a +1| a+2| a+3| a+4 | | 0 | 1 | 2 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: ptr points to first location in pointer array p[]
Expression: **ptr++;
Now we consider expression **ptr++;
before first printf statement.
ptr
is equals top
that is address of first element in array of pointers. Hence,ptr
point to first elementp[0]
in array (or we can sayptr
==&p[0]
).*ptr
meansp[0]
and becausep[0]
isa
, so*ptr
isa
( so*ptr
==a
).And because
*ptr
isa
, then**ptr
is*a
==*(a + 0)
==a[0]
that is0
.Note in expression
**ptr++;
, we do not assign its value to any lhs variable.
So effect of**ptr++;
is simply same asptr++;
==ptr = ptr + 1
=p + 1
In this way after this expressionptr
pointing top[1]
(or we can sayptr
==&p[1]
).
Print-1:
Before first printf things become:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a | a+1| a+2| a+3| a+4 | | 0 | 1 | 2 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: ptr is equals to p + 1 that means it points to p[1]
Now we can understand First printf:
ptr - p
output1
because:ptr = p + 1
, soptr - p
==p + 1 - p
==1
*ptr - a
output1
because:ptr = p + 1
, so*ptr
==*(p + 1)
==p[1]
==a + 1
This means:*ptr - a
=a + 1 - a
==1
**ptr
output1
because:*ptr
==a + 1
from point-2
So**ptr
==*(a + 1)
==a[1]
==1
Expression: *++*ptr;
After first printf we have an expression *++*ptr;
.
As we know from above point-2 that *ptr
== p[1]
.
So, ++*ptr
(that is ++p[1]
) will increments p[1]
to a + 2
Again understand, in expression *++*ptr;
we don't assign its value to any lhs variable so effect of *++*ptr;
is just ++*ptr;
.
Now, before second printf things become:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a |a+2 | a+2| a+3| a+4 | | 0 | 1 | 2 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: p[1] became a + 2
Print-2:
Now we can understand Second printf:
ptr - p
output1
because:ptr = p + 1
, soptr - p
==p + 1 - p
==1
*ptr - a
output2
because:ptr = p + 1
so*ptr
==*(p + 1)
==p[1]
==a + 2
This means:*ptr - a
==a + 2 - a
==2
**ptr
output2
because:*ptr
==a + 2
from point-2
So**ptr
==*(a + 2)
==a[2]
==2
Expression: ++**ptr;
Now expression ++**ptr;
before third printf.
As we know from above point-3 that **ptr
== a[2]
.
So ++**ptr
== ++a[2]
will increments a[2]
to 3
So before third printf things become:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a | a+2| a+2| a+3| a+4 | | 0 | 1 | 3 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: a[2] = 3
Print-3:
Now we can understand Third printf:
ptr - p
output1
because:ptr = p + 1
soptr - p
==p + 1 - p
==1
*ptr - a
output2
because:ptr = p + 1
so*ptr
==*(p + 1)
==p[1]
==a + 2
This means:*ptr - a
=a + 2 - a
==2
**ptr
outputs3
because:*ptr
==a + 2
from point-2
So**ptr
==*(a + 2)
==a[2]
==3
Edit Note: The difference of two pointers has type ptrdiff_t
, and for that, the correct conversion specifier is %td
, not %d
.
An additional point:
I wish to add as I believe it will be helpful for new learners
Suppose we have following two lines with one more 4th printf in you code before return 0;
**++ptr; // additional
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr); // fourth printf
One can check this working code @Codepade , this line outputs 2 2 3
.
Expression: **++ptr;
Because ptr
is equals to p + 1
, after increment ++
operation ptr
becomes p + 2
(or we can say ptr
== &p[2]
).
After that double deference operation **
==> **(p + 2)
== *p[2]
== *(a + 2)
== a[2]
== 3
.
Now, again because we don't have any assignment operation in this statement so effect of expression **++ptr;
is just ++ptr;
.
So thing after expression **++ptr;
becomes as below in figure:
p a
+----+----+----+----+-----+ +----+----+----+----+---+
| a | a+2| a+2| a+3| a+4 | | 0 | 1 | 3 | 3 | 4 |
+----+----+----+----+-----+ +----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
| | | | | | | | | |
p p+1 p+2 p+3 p+4 a a+1 a+2 a+3 a+3
ptr
Notice: ptr is equals to p + 2 that means it points to p[2]
Print-4:
Considering Forth printf I added in question:
ptr - p
output2
because:ptr = p + 2
soptr - p
==p + 2 - p
==2
*ptr - a
output2
because:ptr = p + 2
so*ptr
==*(p + 2)
==p[2]
==a + 2
This means:*ptr - a
=a + 2 - a
==2
**ptr
outputs3
because:*ptr
==a + 2
from above point-2
So**ptr
==*(a + 2)
==a[2]
==3
If you compile with some warnings on (clang didn't even require any flags), you'll see that your program has three extraneous *
operators. Simplifying your crazy-looking expressions yields:
ptr++;
++*ptr;
++**ptr;
And from that, you should be able to see what's happening pretty clearly:
ptr++
just incrementsptr
, so it points to the second element ofp
. After this operationptr - p
will always be1
.++*ptr
increments whatever is pointed to byptr
. That changes the second element ofp
to point to the third element ofa
rather than the second (which it was initialized to). That makes*ptr - a
equal to2
. Likewise**ptr
is the2
froma
.++**ptr
increments whatever is pointed to by whatever is pointed to byptr
. That increments the third element ofa
, making it a3
.
The int*
value at address ptr
has been incremented by the statement *++*ptr;
(actually by the ++*ptr
portion, the leading *
is an unused dereference). Thus the expansion of int *p[]
should now look like this:
int *p[]={a, a+2, a+2, a+3, a+4};
The final ++**ptr;
has now incremented the value at address a+2
, so the original array will now look like this:
int a[]={0,1,3,3,4};