12

I was wondering how *(&array + 1) actually works. I saw this as an easy way to calculate the array length and want to understand it properly before using it. I'm not very experienced with pointer arithmetic, but with my understanding &array gives the address of the first element of the array. (&array + 1) would go to end of the array in terms of address. But shouldn't *(&array + 1) give the value, which is at this address. Instead it prints out the address. I would really appreciate your help to get the pointer stuff clear in my head.

Here is the simple example I'm working on:

int numbers[] = {5,8,9,3,4,6,1};
int length = *(&numbers + 1) - numbers;
dbush
  • 205,898
  • 23
  • 218
  • 273
Varrick
  • 153
  • 8
  • 5
    Any method other than `sizeof(array)/sizeof(array[0])` or similar is not working or having undefined behavior. – Eugene Sh. Oct 09 '18 at 17:27
  • Why doesn't it? With the given code above I get the right length value. – Varrick Oct 09 '18 at 17:27
  • Why do you need this convoluted way if `sizeof(numbers)` gives you right value without this nonsense? – Slava Oct 09 '18 at 17:29
  • The source of this method comes from https://www.geeksforgeeks.org/how-to-find-size-of-array-in-cc-without-using-sizeof-operator/ But I don't really understand the explanation. – Varrick Oct 09 '18 at 17:29
  • 1
    Just split this thing into subexpressions, then you could ask a better question concerning each subexpression. BTW: Remove one of the language tags, C and C++ are different languages. – Ulrich Eckhardt Oct 09 '18 at 17:29
  • Your code is performing pointer arithmetics with an invalid pointer (`*(&numbers + 1)` - is not pointing to an object) – Eugene Sh. Oct 09 '18 at 17:30
  • You are essentially treating numbers[] as a 2-d array with 1 row. *(&numbers + 1) is the address of the start of the second row, which doesn't exist, but which gives the right value. – Lee Daniel Crocker Oct 09 '18 at 17:30
  • @user2079303 I guess I missed the c++ tag – Eugene Sh. Oct 09 '18 at 17:31
  • 2
    Use `std::size(numbers)`. Arrays decay to pointer in many cases, where such hacks, as presented by this question and comments, silently fail. `std::size()` will raise a compiler error instead. – zett42 Oct 09 '18 at 17:31
  • @ Lee Daniel Crocker Thank you, with the answer of Brian this really helps to imagine that thing – Varrick Oct 09 '18 at 17:36

3 Answers3

9

(This answer is for C++.)

  1. &numbers is a pointer to the array itself. It has type int (*)[7].
  2. &numbers + 1 is a pointer to the byte right after the array, where another array of 7 ints would be located. It still has type int (*)[7].
  3. *(&numbers + 1) dereferences this pointer, yielding an lvalue of type int[7] referring to the byte right after the array.
  4. *(&numbers + 1) - numbers: Using the - operator forces both operands to undergo the array-to-pointer conversion, so pointers can be subtracted. *(&numbers + 1) is converted to an int* pointing at the byte after the array. numbers is converted to an int* pointing at the first byte of the array. Their difference is the number of ints between the two pointers---which is the number of ints in the array.

Edit: Although there's no valid object pointed to by &numbers + 1, this is what's called a "past the end" pointer. If p is a pointer to T, pointing to a valid object of type T, then it's always valid to compute p + 1, even though *p may be a single object, or the object at the end of an array. In that case, you get a "past the end" pointer, which does not point to a valid object, but is still a valid pointer. You can use this pointer for pointer arithmetic, and even dereference it to yield an lvalue, as long as you do not try to read or write through that lvalue. Note that you can only go one byte past-the-end of an object; attempting to go any further leads to undefined behaviour.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
7

The expression &numbers gives you the address of the array, not the first member (although numerically they are the same). The type of this expression is int (*)[7], i.e. a pointer to an array of size 7.

The expression &numbers + 1 adds sizeof(int[7]) bytes to the address of array. The resulting pointer points right after the array.

The problem however is when you then dereference this pointer with *(&numbers + 1). Dereferencing a pointer that points one element past the end of an array invokes undefined behavior.

The proper way to get the number of elements of an array is sizeof(numbers)/sizeof(numbers[0]). This assumes that the array was defined in the current scope and is not a parameter to a function.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • 2
    While that is proper for C, I would assert that [`std::size()`](https://en.cppreference.com/w/cpp/iterator/size) is the proper way for C++ since ist debut in C++17. – Deduplicator Oct 09 '18 at 17:43
  • 2
    @Deduplicator: and before that, you could implement an equivalent as: `template size_t size(T (&array)[N]) { return N; }` (legal C++98). This has a substantial advantage over `sizeof(array)/sizeof(array[0])`--it won't compile if you accidentally attempt to apply it to a pointer instead of an actual array. – Jerry Coffin Oct 09 '18 at 18:19
2

but with my understanding &array gives the address of the first element of the array.

This understanding is misleading. &array gives the address of the array. Sure, the value of that address is the same same as the first element, but the type of the expression is different. The type of the expression &array is "pointer to array of N elements of type T" (where N is the length that you're looking for and T is int).

But shouldn't *(&array + 1) give the value, which is at this address.

Well yes... but it's here that the type of the expression becomes important. Indirecting a pointer to an array (rather than pointer to an element of the array) will result in the array itself.

In the subtraction expression, both array operands decay into pointer to first element. Since the subtraction uses decayed pointers, the unit of the pointer arithmetic is in terms of the element size.

I saw this as an easy way to calculate the array length

There are easier ways:

std::size(numbers)

And in C:

sizeof(numbers)/sizeof(numbers[0])
eerorika
  • 232,697
  • 12
  • 197
  • 326