Search code examples
c++pointersmultidimensional-arraymemory-addressdereference

What are the underlying types of pointers to 1D/2D arrays, and why is double dereferencing needed?


Say I initialise arrays and variables as such:

int arr1d[3] = { 1, 2, 3 };
int* arrptr = arr1d;
int (*ptr1d)[3] = &arr1d;

int arr2d[2][3] = { {1, 2, 3}, {4, 5, 6} };
int (*ptr2d)[3] = arr2d;

So, arr1d is an array of 3 integers. Under the hood, arr1d is a pointer to the first element of the array, i.e. &arr1d[0]. Hence, we can assign arr1d to int* arrptr.

ptr1d points to an array of 3 integers. arr1d is an int* pointing to &arr1d[0], but why is &arr1d of type int (*)[3]? Naively, I thought that writing &arr1d simply gave us an address to assign to the pointer. Is this is something specific to pointers to arrays?

Apparently, arr2d is of type int (*)[3] (which I learned from this post). Both ptr2d and *ptr2d have the same value, so why is that second dereference even necessary? My thoughts are that it may be because ptr2d is of type int (*)[3] (pointer to array of 3 ints), *ptr2d is of type int* (pointer to an int), and so **ptr2d ultimately gets us the first value of the first array, 1. So we're not dereferencing here to walk along addresses as we see in many diagrams, we're dereferencing to access underlying types. Is this correct? Does this explain why the type of &arr1d is also int (*)[3] (we're going "up a level"?)

Thank you.


Solution

  • int arr1d[3] = { 1, 2, 3 };
    

    So, arr1d is an array of 3 integers.

    Correct.

    Under the hood, arr1d is a pointer to the first element of the array, i.e. &arr1d[0]

    It is unclear what you mean by "under the hood", but in the C++ language an array is an array. It is not a pointer type. Saying that arr1d is &arr1d[0] is wrong. If you mean in machine code, then it is wrong because machine code has neither types nor variables.

    Hence, we can assign arr1d to int* arrptr.

    We can assign arr1d to int* arrptr because an array type is implicitly convertible to a pointer to its element type. And the result of this conversion is indeed same value and type as &arr1d[0]. This conversion is called decaying.

    Naively, I thought that writing &arr1d simply gave us an address to assign to the pointer.

    &arr1d simply does give us an address that can be assigned to a pointer. So, you thought correctly.

    but why is &arr1d of type int (*)[3]?

    The type of arr1d is array of 3 integers i.e. int[3]. Taking its address gives you a pointer to array of 3 integers i.e. int (*)[3]).

    Is this is something specific to pointers to arrays?

    The unary & always gives a pointer to the operand (except in case of class types for which the operator has been overloaded in which case it gives whatever the overload returns; overloading unary & is rare). It is called the addressof operator.

    Of course, & gives you a pointer to an array only when operand is an array. Other times it gives some other pointer type.

    int (*ptr2d)[3] = arr2d;
    

    Apparently, arr2d is of type int (*)[3] (which I learned from this post).

    You learned wrongly. The type of arr2d is int[2][3]. int[3] is the type of the element of arr2d and thus int (*)[3] is the pointer type to which the array type decays; a pointer to element of that array.

    *ptr2d is of type int* (pointer to an int)

    Wrong. *ptr2d is of type int[3] (array of 3 int). This array does implicitly convert to pointer to its first element, which has the type int*.