Pointer Refresher
A pointer is just a numeric value that holds the address of a value of type T
. This means that T
can also be a pointer type, thus creating pointers-to-pointers, pointers-to-pointers-to-pointers, and crazy things like char**********
- which is simply a pointer (T*
) where T
is a pointer to something else (T = E*
) where E
is a pointer to something else (and so on...).
Something to remember here is that a pointer itself is a value and thus takes space. More specifically, it's (usually) the size of the addressable space the CPU supports.
So for example, the 6502 processor (commonly found in old gaming consoles like the NES and Atari, as well as the Apple II, etc.) could only address 16 bits of memory, and thus its "pointers" were 16-bits in size.
So regardless of the underlying type, a pointer will (usually) be as large as the addressable space.
Keep in mind that a pointer doesn't guarantee that it points to valid memory - it's simply a numeric value that happens to specify a location in memory.
Array Refresher
An array is simply a series of T
elements in contiguously addressable memory. The fact it's a "double pointer" (or pointer-to-a-pointer) is innocuous - it is still a regular pointer.
For example, allocating an array of 3 T
's will result in a memory block that is 3 * sizeof(T)
bytes long.
When you malloc(...)
that memory, the pointer returned simply points to the first element.
T *array = malloc(3 * sizeof(T));
printf("%d\n", (&array[0] == &(*array))); // 1 (true)
Keep in mind that the subscript operator (the [...]
) is basically just syntactic sugar for:
(*(array + sizeof(*array) * n)) // array[n]
Arrays of Pointers
To sum all of this up, when you do
E **array = malloc(3 * sizeof(E*));
You're doing the same thing as
T *array = malloc(3 * sizeof(T));
where T
is really E*
.
Two things to remember about malloc(...)
:
- It doesn't initialize the memory with any specific values (use
calloc
for that)
- It's not guaranteed (nor really even common) for the memory to be contiguous or adjacent to the memory returned by a previous call to
malloc
Therefore, when you fill the previously created array-of-pointers with subsequent calls to malloc()
, they might be in arbitrarily random places in memory.
All you're doing with your first malloc()
call is simply creating the block of memory required to store n
pointers. That's it.
To answer your questions...
If we dereference pointer
, by doing *pointer
what do we get? Do we get pointer[0]
?
Since pointer
is just a int**
, and remembering that malloc(...)
returns the address of the first byte in the block of memory you allocated, *pointer
will indeed evaluate to pointer[0]
.
And if so, what is pointer[0]
?
Again, since pointer
as the type int**
, then pointer[0]
will return a value type of int*
with the numeric contents of the first sizeof(int*)
bytes in the memory block pointed to by pointer
.
If I were to print the contents of pointer
would it show me 0x0010
?
If by "printing the contents" you mean printf("%p\n", (void*) pointer)
, then no.
Since you malloc()
'd the memory block that pointer
points to, pointer
itself is just a value with the size of sizeof(int**)
, and thus will hold the address (as a numeric value) where the block of memory you malloc()
'd resides.
So the above printf()
call will simply print that value out.
What about pointer[0]
?
Again assuming you mean printf("%p\n", (void*) pointer[0])
, then you'll get a slightly different output.
Since pointer[0]
is the equivalent of *pointer
, and thus causes pointer
to be dereferenced, you'll get a value of int*
and thus the pointer value that is stored in the first element.
You would need to further dereference that pointer to get the numeric value stored in the first integer that you allocated; for example:
printf("%d\n", **pointer);
// or
printf("%d\n", *pointer[0]);
// or even
printf("%d\n", pointer[0][0]); // though this isn't recommended
// for readability's sake since
// `pointer[0]` isn't an array but
// instead a pointer to a single `int`.