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 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
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 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
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 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.
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 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