0

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.

Tom
  • 355
  • 4
  • 11
  • A 2d array *could* be implemented as an array of pointers to memory. In this implementation you would need to have a "double dereference". If the array is implemented as a giant block of memory, you don't need to double dereference, you'll just need to calculate the address. – Thomas Matthews Sep 03 '20 at 16:39
  • *Under the hood, `arr1d` is a pointer to the first element of the array* Best to avoid thinking that because it's not true and will trip you up. Arrays decay to pointers, but they are not pointers. – user4581301 Sep 03 '20 at 16:41
  • *Apparently, `arr2d` is of type `int (*)[3]`*, You have misunderstood. A pointer to `arr2d` is of type `int (*)[3]`. `arr2d` is still `int [2][3]`. You can test this by taking the `sizeof` of both and watching what happens – user4581301 Sep 03 '20 at 16:48
  • Important reading: [What is array to pointer decay?](https://stackoverflow.com/questions/1461432/what-is-array-to-pointer-decay) – user4581301 Sep 03 '20 at 16:49
  • Of course 2D arrays could be implemented as an array of pointers to memory, but I'm asking in the context of a contiguous array where it seems to be necessary. So ... If a 1D array decays to a simple pointer, what does a 2D array decay to? From my understanding (also mentioned in the question you linked), they decay to the type of their initial element, and since the first element of `arr2d` is an array, here I am correct to say that it decays to `int (*)[3]`, no? Otherwise how do we explain the difference in assigning to `ptr1d` and `ptr2d`, and why the two dereferences are needed? – Tom Sep 03 '20 at 17:08
  • @TomH, if you had something like `int arr1d[3][3]` it would still *decay* to a pointer to the first element, but the first element is an array of 3 `int`s – Geno C Sep 03 '20 at 17:13

1 Answers1

5
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*.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Great answer. I appreciate it greatly that you systemically broke down where my thinking was incorrect. I have one immediate question, though: could you clarify what you mean by "`ptr1d` *is* an array of 3 integers ... It doesn't point"? How is it not a pointer type? – Tom Sep 03 '20 at 17:28
  • 1
    @TomH Sorry, the variable names are quite similar, so I made a mistake. `ptr1d` is of course a pointer. I thought I was writing about `arr1d`. Answer is fixed. – eerorika Sep 03 '20 at 17:36