I'm messing around with multidimensional arrays and pointers. I've been looking at a program that prints out the contents of, and addresses of, a simple array. Here's my array declaration:
int zippo[4][2] = { {2,4},
{6,8},
{1,3},
{5,7} };
My current understanding is that zippo
is a pointer, and it can hold the address of a couple of other pointers. By default, zippo
holds the address of pointer zippo[0]
, and it can also hold the addresses of pointers zippo[1]
, zippo[2]
, and zippo[3]
.
Now, take the following statement:
printf("zippo[0] = %p\n", zippo[0]);
printf(" *zippo = %p\n", *zippo);
printf(" zippo = %p\n", zippo);
On my machine, that gives the following output:
zippo[0] = 0x7fff170e2230
*zippo = 0x7fff170e2230
zippo = 0x7fff170e2230
I perfectly understand why zippo[0]
and *zippo
have the same value. They're both pointers, and they both store the address (by default) of the integer 2, or zippo[0][0]
. But what is up with zippo
also sharing the same memory address? Shouldn't zippo
be storing the address of the pointer zippo[0]
? Whaaaat?
Very well explained by Reed, I shall add few more points to make it simpler, when we refer to
zippo
orzippo[0]
orzippo[0][0]
, we are still referring to the same base address of the arrayzippo
. The reason being arrays are always contiguous block of memory and multidimensional arrays are multiple single dimension arrays continuously placed.When you have to increment by each row, you need a pointer
int *p = &zippo[0][0]
, and doingp++
increments the pointer by every row. In your example id its a 4 X 2 array, on doingp++
its, pointer currently points to second set of 4 elements.zippo
is not a pointer. It's an array of array values.zippo
, andzippo[i]
fori
in 0..4 can "decay" to a pointer in certain cases (particularly, in value contexts). Try printingsizeof zippo
for an example of the use ofzippo
in a non-value context. In this case,sizeof
will report the size of the array, not the size of a pointer.The name of an array, in value contexts, decays to a pointer to its first element. So, in value context,
zippo
is the same as&zippo[0]
, and thus has the type "pointer to an array [2] ofint
";*zippo
, in value context is the same as&zippo[0][0]
, i.e., "pointer toint
". They have the same value, but different types.I recommend reading Arrays and Pointers for answering your second question. The pointers have the same "value", but point to different amounts of space. Try printing
zippo+1
and*zippo+1
to see that more clearly:For my run, it prints:
Telling me that
sizeof(int)
on my machine is 4, and that the second and the third pointers are not equal in value (as expected).Also,
"%p"
format specifier needsvoid *
in*printf()
functions, so you should cast your pointers tovoid *
in yourprintf()
calls (printf()
is a variadic function, so the compiler can't do the automatic conversion for you here).Edit: When I say an array "decays" to a pointer, I mean that the name of an array in value context is equivalent to a pointer. Thus, if I have
T pt[100];
for some typeT
, then the namept
is of typeT *
in value contexts. Forsizeof
and unary&
operators, the namept
doesn't reduce to a pointer. But you can doT *p = pt;
—this is perfectly valid because in this context,pt
is of typeT *
.Note that this "decaying" happens only once. So, let's say we have:
Then,
zippo
in value context decays to a pointer of type: pointer to array[2] ofint
. In code:is valid, whereas
will trigger an "incompatible pointer assignment" warning.
With
zippo
defined as above,are all valid. They should print the same value when printed using
printf("%p\n", (void *)name);
, but the pointers are different in that they point to the whole matrix, a row, and a single integer respectively.When an array expression appears in most contexts, its type is implicitly converted from "N-element array of T" to "pointer to T", and its value is set to point to the first element in the array. The exceptions to this rule are when the array expression is an operand of either the
sizeof
or address-of (&
) operators, or when the array is a string literal being used as an initializer in a declaration.Thus, the expression
zippo
"decays" from typeint [4][2]
(4-element array of 2-element arrays of int) toint (*)[2]
(pointer to 2-element array of int). Similarly, the type ofzippo[0]
isint [2]
, which is implicitly converted toint *
.Given the declaration
int zippo[4][2]
, the following table shows the types of various array expressions involving zippo and any implicit conversions:Note that
zippo
,&zippo
,*zippo
,zippo[0]
,&zippo[0]
, and&zippo[0][0]
all have the same value; they all point to the base of the array (the address of the array is the same as the address of the first element of the array). The types of the various expressions all differ, though.When you declare a multidimensional array, the compiler treats it as a single dimensional array. Multidimensional arrays are just an abstraction to make our life easier. You have a misunderstanding: This isn't one array pointing to 4 arrays, its always just a single contigous block of memory.
In your case, doing:
Is really the same as doing
With the math required for the 2D addressing handled for you by the compiler.
For details, see this tutorial on Arrays in C++.
This is very different than doing:
or
In this case, you're making an array of four pointers, which could be allocated to other arrays.
The important thing here is that
int zippy[4][2]
is not the same type of object asint **zippo
.Just like
int zippi[5]
,zippy
is the address of a block of memory. But the compiler knows that you want to address the eight memory location starting atzippy
with a two dimensional syntax, but want to address the five memory location starting atzippi
with a one dimensional syntax.zippo
is a different thing entirely. It holds the address of a a block of memory big enough to contain two pointer, and if you make them point at some arrays of integers, you can dereference them with the two dimensional array access syntax.