Alright. Let's talk about some basic issues first:
On many systems, int
will take up less room than void *
. So you're possibly truncating your pointers when you print them. It's quite easy to fix that, so let's do it.
Additionally, it's unnecessary to cast the value of malloc()
, so let's get rid of that as well to clean up the code a bit.
Finally, as chris notes, to use the %p
format specifier, we need to cast the int **
and int *
variables to void *
.
Fixed foo.c
#include <stdio.h>
#include <stdlib.h>
int main() {
int **p = malloc(sizeof(int *) * 2);
int i, j, c = 1;
for (i = 0; i < 2; i++)
p[i] = malloc(sizeof(int) * 2);
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
p[i][j] = c++;
printf("%p %p %p %d\n", (void *) &p[0][0], (void *) p, (void *) *p, **p);
}
Which now outputs:
0x7fd193c03930 0x7fd193c03920 0x7fd193c03930 1
So now, your actual question:
First, let's talk about what address &p[0][0]
points to. That's a fairly complicated expression, but we can reduce it using a fairly simple process such that we end up with *p
or p[0]
.
&p[0][0] == &*(p[0] + 0) == p[0] + 0 == p[0] == *(p + 0) == *p
Looking at that, it should be pretty clear why &p[0][0]
(the first argument) and *p
(the third argument) print the same value.
Now, I don't see any good reason why p[0]
should point to the same address as p
. p
and p[0]
have been assigned values from separate calls to malloc()
.