In a project, somebody pushed this line:
double (*e)[n+1] = malloc((n+1) * sizeof(*e));
Which supposedly creates a two-dimensional array of (n+1)*(n+1) doubles.
Supposedly, I say, because so far, nobody I asked could tell me what this does, exactly, nor where it originated from or why it should work (which allegedly, it does, but I'm not yet buying it).
Perhaps I'm missing something obvious, but I'd appreciate it if somebody could explain above line to me. Because personally, I'd feel much better if we'd use something we actually understand.
This is the typical way you should allocate 2D arrays dynamically.
e
is an array pointer to an array of typedouble [n+1]
.sizeof(*e)
therefore gives the type of the pointed-at type, which is the size of onedouble [n+1]
array.n+1
such arrays.e
to point at the first array in this array of arrays.e
ase[i][j]
to access individual items in the 2D array.Personally I think this style is much easier to read:
The variable
e
is a pointer to an array ofn + 1
elements of typedouble
.Using the dereference operator on
e
gives you the base-type ofe
which is " array ofn + 1
elements of typedouble
".The
malloc
call simply takes the base-type ofe
(explained above) and gets its size, multiplies it byn + 1
, and passing that size to themalloc
function. Essentially allocating an array ofn + 1
arrays ofn + 1
elements ofdouble
.This idiom falls naturally out of 1D array allocation. Let's start with allocating a 1D array of some arbitrary type
T
:Simple, right? The expression
*p
has typeT
, sosizeof *p
gives the same result assizeof (T)
, so we're allocating enough space for anN
-element array ofT
. This is true for any typeT
.Now, let's substitute
T
with an array type likeR [10]
. Then our allocation becomesThe semantics here are exactly the same as the 1D allocation method; all that's changed is the type of
p
. Instead ofT *
, it's nowR (*)[10]
. The expression*p
has typeT
which is typeR [10]
, sosizeof *p
is equivalent tosizeof (T)
which is equivalent tosizeof (R [10])
. So we're allocating enough space for anN
by10
element array ofR
.We can take this even further if we want; suppose
R
is itself an array typeint [5]
. Substitute that forR
and we getSame deal -
sizeof *p
is the same assizeof (int [10][5])
, and we wind up allocating a contiguous chunk of memory large enough to hold aN
by10
by5
array ofint
.So that's the allocation side; what about the access side?
Remember that the
[]
subscript operation is defined in terms of pointer arithmetic:a[i]
is defined as*(a + i)
1. Thus, the subscript operator[]
implicitly dereferences a pointer. Ifp
is a pointer toT
, you can access the pointed-to value either by explicitly dereferencing with the unary*
operator:or by using the
[]
subscript operator:Thus, if
p
points to the first element of an array, you can access any element of that array by using a subscript on the pointerp
:Now, let's do our substitution operation again and replace
T
with the array typeR [10]
:One immediately apparent difference; we're explicitly dereferencing
p
before applying the subscript operator. We don't want to subscript intop
, we want to subscript into whatp
points to (in this case, the arrayarr[0]
). Since unary*
has lower precedence than the subscript[]
operator, we have to use parentheses to explicitly groupp
with*
. But remember from above that*p
is the same asp[0]
, so we can substitute that withor just
Thus, if
p
points to a 2D array, we can index into that array throughp
like so:Taking this to the same conclusion as above and substituting
R
withint [5]
:This works just the same if
p
points to a regular array, or if it points to memory allocated throughmalloc
.This idiom has the following benefits:
free
. Again, not true with the piecemeal allocation method, where you have to deallocate eacharr[i]
before you can deallocatearr
.Sometimes the piecemeal allocation method is preferable, such as when your heap is badly fragmented and you can't allocate your memory as a contiguous chunk, or you want to allocate a "jagged" array where each row can have a different length. But in general, this is the better way to go.
1. Remember that arrays are not pointers - instead, array expressions are converted to pointer expressions as necessary.