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[]
Now we consider expression **ptr++;
before first printf statement.
ptr
is equals to p
that is address of first element in array of pointers. Hence, ptr
point to first element p[0]
in array (or we can say ptr
== &p[0]
).
*ptr
means p[0]
and because p[0]
is a
, so *ptr
is a
( so *ptr
== a
).
And because *ptr
is a
, then **ptr
is *a
== *(a + 0)
== a[0]
that is 0
.
Note in expression **ptr++;
, we do not assign its value to any lhs variable.
So effect of **ptr++;
is simply same as ptr++;
== ptr = ptr + 1
= p + 1
In this way after this expression ptr
pointing to p[1]
(or we can say ptr
== &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
output 1
because:ptr = p + 1
, so ptr - p
== p + 1 - p
== 1
*ptr - a
output 1
because:ptr = p + 1
, so *ptr
== *(p + 1)
== p[1]
== a + 1
This means: *ptr - a
= a + 1 - a
== 1
**ptr
output 1
because:*ptr
== a + 1
from point-2
So **ptr
== *(a + 1)
== a[1]
== 1
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
output 1
because:ptr = p + 1
, so ptr - p
== p + 1 - p
== 1
*ptr - a
output 2
because:ptr = p + 1
so *ptr
== *(p + 1)
== p[1]
== a + 2
This means: *ptr - a
== a + 2 - a
== 2
**ptr
output 2
because:*ptr
== a + 2
from point-2
So **ptr
== *(a + 2)
== a[2]
== 2
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
output 1
because:ptr = p + 1
so ptr - p
== p + 1 - p
== 1
*ptr - a
output 2
because:ptr = p + 1
so *ptr
== *(p + 1)
== p[1]
== a + 2
This means: *ptr - a
= a + 2 - a
== 2
**ptr
outputs 3
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
.
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
output 2
because:ptr = p + 2
so ptr - p
== p + 2 - p
== 2
*ptr - a
output 2
because:ptr = p + 2
so *ptr
== *(p + 2)
== p[2]
== a + 2
This means: *ptr - a
= a + 2 - a
== 2
**ptr
outputs 3
because:*ptr
== a + 2
from above point-2
So **ptr
== *(a + 2)
== a[2]
== 3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With