Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does *(&arr + 1) - arr give the length in elements of array arr?

Tags:

c++

arrays

#include <iostream>
using namespace std;

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

For the code above, I don't quite understand what these two pieces of codes are doing:

*(&arr + 1) 

and

*(&arr)
&arr

Could someone explains? Because when I run the following two codes, I get the same output for the following:

&arr (I think this point to the address of the first element of arr)

*(&arr) then I don't quite understand what this do, what does the symbol * do to &arr (i.e. to the address here)?, because the two outputs are the same when I run them

and finally what is it exactly happening when an integer say 1 is added to the address by this code here: &arr + 1

like image 837
john_w Avatar asked Apr 15 '20 20:04

john_w


3 Answers

This is a mine field, but I'll give it a try:

  • &arr returns a pointer to an int[5]
  • + 1 steps the pointer one int[5]
  • *(&arr + 1) dereferences the result back to an int(&)[5]
    I don't know if this causes undefined behavior, but if it doesn't, the next step will be:
  • *(&arr + 1) - arr does pointer arithmetics after the two int[5]'s have decayed to int pointers, returning the diff between the two int pointers, which is 5.

Rewritten to make it a bit clearer:

int  arr[5] = {5, 8, 1, 3, 6};

int (*begin_ptr)[5] = &arr + 0;     // begin_ptr is a  int(*)[5]
int (*end_ptr)[5]   = &arr + 1;     // end_ptr is a    int(*)[5]

// Note:
//       begin_ptr + 1        ==  end_ptr
//       end_ptr - begin_ptr  ==  1

int (&begin_ref)[5] = *begin_ptr;   // begin_ref is a  int(&)[5]
int (&end_ref)[5]   = *end_ptr;     // end_ref is a    int(&)[5]   UB here?

auto len = end_ref - begin_ref; // the array references decay into int*
std::cout << "The length of the array is: " << len << '\n'; // 5

I'll leave the question if it's UB or not open but referencing an object before the referenced storage has been allocated does look a bit suspicious.

like image 158
Ted Lyngmo Avatar answered Sep 30 '22 17:09

Ted Lyngmo


Example:

int  arr[] = {1, 2, 3, 4, 5, 6}; 
int size = *(&arr + 1) - arr; 

Here the pointer arithmetic does its part. We don’t need to explicitly convert each of the locations to character pointers.

&arr ==> Pointer to an array of 6 elements. [See this for difference between &arr and arr]

(&arr + 1) ==> Address of 6 integers ahead as pointer type is pointer to array of 6 integers.

*(&arr + 1) ==> Same address as (&arr + 1), but type of pointer is "int *".

*(&arr + 1) - arr ==> Since *(&arr + 1) points to the address 6 integers ahead of arr, the difference between two is 6.

like image 38
Shivam Jha Avatar answered Sep 30 '22 18:09

Shivam Jha


Given the following facts:

  • When you increment/decrement a pointer by an integral value X, the value of the pointer is increased/decreased by X times the number of bytes of the type the pointer is pointing at.

  • When you subtract 2 pointers of the same type, the result is the difference between their held addresses, divided by the number of bytes of the type being pointed at.

  • When you refer to an array by its name alone, it decays into a pointer to the array's 1st element.

The type of your arr variable is int[5], ie an array of 5 ints. &arr returns an int[5]* pointer to arr (technically, it is actually written like int(*)[5], but lets not worry about that here, for simplicity). Lets call this pointer temp below.

Then, the + 1 increments the value of temp by 1 int[5] element. In other words, the address stored in temp is increased by 1 * sizeof(int[5]), or 1 * (sizeof(int) * 5), number of bytes. This effectively gives you an int[5]* pointer to the end of arr (ie, to &arr[5]). No int[5] element physically exists at that memory address, but it is legal to create a pointer to it, for purposes of pointer arithmetic.

Dereferencing temp gives you a reference to an int[5] at the end of arr. That reference decays into an int* pointer when passed to operator-.

In - arr, the reference to arr decays into an int* pointer to arr[0] when passed to operator-.

Thus, given this code:

int len = *(&arr + 1) - arr;

Which is effectively the same as this:

int len = &arr[5] - &arr[0];

Which is effectively the same as this:

int len = (<address of arr[5]> - <address of arr[0]>) / sizeof(int);

Thus, the result is 5.

like image 33
Remy Lebeau Avatar answered Sep 30 '22 18:09

Remy Lebeau