First, some background information:
Except when it is the operand of the sizeof
, _Alignof
, or unary &
operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T
" is converted ("decays") to an expression of type "pointer to T
", and its value is the address of the first element in the array. For example, given the array
int a[10];
anytime the expression a
appears in the code, its type will be converted from "10-element array of int
" to "pointer to int
", or int *
, except for cases like sizeof a
, _Alignof a
, and &a
. If we have a 2D array of T, such as
int a[10][10];
the expression a
will be converted from type "10-element array of 10-element array of int
" to "pointer to 10-element array of int
", or int (*)[10]
(look familiar? that's the type of your pointer p
).
If we want to dynamically allocate an N-element array of type T
, we write something like
T *p = malloc(N * sizeof *p);
sizeof *p
is equivalent to sizeof (T)
. In this particular case, type T
is "10-element array of int
", or int [10]
. We want to allocate 4 such arrays, so we can write
int (*p)[10];
p = malloc(4 * sizeof *p);
This allocates space for 4 10-element arrays of int
, and assigns the result to p
. (sizeof *p == sizeof (int [10])
).
So how does this become a 2D array?
Remember that the expression a[i]
is equivalent to *(a + i)
; we find the address of the i
'th element of type T
following a
and dereference the result. In this case p[i]
gives us the address of the i
th 10-element array of int
following p
. Since we dereference the pointer as part of the subscript operation, the type of the expression p[i]
is "10-element array of int
". Thus we can subscript this expression again and get p[i][j]
.