Reputation: 3749
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.
Upvotes: 31
Views: 7052
Reputation: 58261
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
Upvotes: 54
Reputation: 56049
Remember that ++
has higher precedence than *
, so when you do **ptr++
, that increments the pointer and double-dereferences the old value, which will do nothing more than crash if it's not a valid pointer-to-pointer, and your compiler (at least with warnings enabled) should warn you of an unused result.
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 = &(p[0]); *ptr = a; **ptr = 0.
**ptr++; // ptr = &(p[1]); *ptr = a+1; **ptr = 1
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);
*++*ptr; // ptr = &(p[1]); *ptr = a+2; **ptr = 2; p = {a, a+2, a+2, a+3, a+4}
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);
++**ptr; // ptr = &(p[1]); *ptr = a+2; **ptr = 3; a = {0, 1, 3, 3, 4}
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);
Upvotes: 2
Reputation: 1927
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};
Upvotes: 2
Reputation: 224854
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 increments ptr
, so it points to the second element of p
. After this operation ptr - p
will always be 1
.
++*ptr
increments whatever is pointed to by ptr
. That changes the second element of p
to point to the third element of a
rather than the second (which it was initialized to). That makes *ptr - a
equal to 2
. Likewise **ptr
is the 2
from a
.
++**ptr
increments whatever is pointed to by whatever is pointed to by ptr
. That increments the third element of a
, making it a 3
.
Upvotes: 8