Why does * need to be put before (&a) to subtract a (a is an array)?

Viewed 187

I was learning how to find the length of an array and I'm baffled by this solution. I tried to find an explanation online but there seems to be none.

int arr[5] = {5, 8, 1, 3, 6};
   int len = *(&arr + 1) - arr;
   cout << "The length of the array is: " << len;
3 Answers

The memory address of the array is the same as the memory address of the first element, and when you add to or subtract from a pointer, it is done by the size of the type it points to, so:

  • arr refers to int, and &arr refers to int[5].
  • &arr+1 increments the memory address in the size of five integers.
  • If you do (&arr+1)-arr you get a compile error, because they are different types.
  • If you do (&arr+1)-&arr you get 1, because the offset of the memory address is the same as one size of int[5].
  • Therefore, when you do *(&arr+1), you get the same memory address but pointing to int and not int[5]. Now you wont get a compile error, because both pointers point to int and you get the offset of the memory address in terms of int size, and not int[5]. Memory addresses and types are quite difficult to explain sometimes, I hope I made it clear. Here you have some code you can run to see some of the concepts mentioned:
   int arr[5] = {5, 8, 1, 3, 6};
   int len = *(&arr + 1) - arr;
   
   cout << "arr: " << arr << endl;
   cout << "arr + 1: " << arr+1 << endl;
   cout << "&arr: " << &arr << endl;
   cout << "&arr + 1: " << &arr+1 << endl;
   cout << "*(&arr + 1): " << *(&arr+1) << endl;
   
   // cout << "&arr + 1 - arr: " << &arr+1-arr << endl;
   // error: invalid operands of types ‘int (*)[5]’ and ‘int [5]’ to binary ‘operator-’

   cout << "The length of the array is: " << len;

The type of the array arr is int[5], the type of &arr is int(*)[5]. (&arr + 1) increases the array address on sizeof(int[5]) as it's done by the rules of the pointer arithmetic, i.e. computes the address after the array. *(&arr + 1) is int[5], an array right after arr, where arr[5] would place. The both arguments of the substractions decay to int*. The substraction of pointers to int gives 5.

This may be considered as undefined behavior, since substraction of pointers belonging to different object storages is not defined. Also results of expressions with pointers addressing unallocated memory (like the (&arr + 1)) are undefined.

First, the traditional way to get the size of an array is sizeof a/sizeof *a. C++11 adds std::extent<decltype(a)>::value. There is of course no way to get an array’s size from just a pointer to it, as in

void f(int x[]) {/* no size here */}

Since an array is not a suitable operand to -, the array-to-pointer conversion occurs for the right-hand operand, producing an int*. Both operands must be of this type for the subtraction to result in a number of ints. &arr is of course a pointer to the array (of type int(*)[5], which conveys the size), and so therefore is &arr+1. Adding a * (or, equivalently, writing (&arr)[1]) produces an lvalue that supposedly refers to “the next array after arr”, which itself decays to a pointer that works with -.

However, as the indexing form indicates, this involves referring to an array that does not exist and is thus undefined behavior.

Related