9

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;
Boann
  • 48,794
  • 16
  • 117
  • 146
Dat Le
  • 111
  • 5
  • 2
    I am baffled as to why this code keeps showing up. There is one great and easy to understand way to get size of the array: `sizeof(array)/sizeof(*array)`. This is simple and clean. To the contrary, it is not clear even for experts in C++ if the code shown above is legal or not. – SergeyA Aug 17 '20 at 01:26
  • That solution is good too. It's just that I want to understand both. – Dat Le Aug 17 '20 at 01:40
  • 1
    The best solution is `std::size(arr)` ( https://en.cppreference.com/w/cpp/iterator/size ). No need to worry about extra knowledge, it just works as expected. – Justin Aug 17 '20 at 02:53

3 Answers3

7

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;
Miguel
  • 2,130
  • 1
  • 11
  • 26
  • Hey, thanks for the answer. It helped me a lot. But I just wanna ask where did you learn this from? Is there a book or video talks about stuff like this? – Dat Le Aug 17 '20 at 00:59
  • I'm afraid I don't have anything specific to recommend... I learned this kind of stuff from university professors, I don't know where to find it written or in video, I'm sorry. – Miguel Aug 17 '20 at 01:03
  • &arr is a pointer to int[5] (that means int(*)[5]), so when you derreference it you get the type int[5], and I think that is implicitly casted to an int pointer when you use arithmetic operators. The answer of @S.M. explains it. Also, if you want some more information, you can look up about pointer operations and arithmetic. – Miguel Aug 17 '20 at 01:18
6

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.

273K
  • 29,503
  • 10
  • 41
  • 64
  • 1
    "*subscription of pointers is not defined*" - what do you mean by that? – Fureeish Aug 16 '20 at 23:59
  • 6
    I believe it is a known language defect that whether or not this is UB is ambiguous. I asked a similar question a while ago : [Dereferencing one past the end pointer to array type](https://stackoverflow.com/q/52727045/7359094). – François Andrieux Aug 17 '20 at 00:01
  • Subscription - this is the misprint caused by the autocorrection. – 273K Aug 17 '20 at 00:03
  • Ok, updated to pointers not belonging to the same array. – 273K Aug 17 '20 at 00:06
  • @FrançoisAndrieux • that's an interesting cobwebby corner of the language. To my eye (as I am no longer a language lawyer), it seems to be UB. – Eljay Aug 17 '20 at 00:26
1

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.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76