-6

Can someone please explain why the following code successfully prints the length of arr[]?

#include<stdio.h>

int main()
{
  int arr[] = {1,2,3,4,5,6,9,8,7};

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

  printf("%d\n",size);
}

I'm specifically interested in the memory layout behind these operations.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Please explain what you mean by 'how its working' more precise. –  Nov 01 '17 at 13:55
  • 3
    Think about how a pointer arithmetic is working and what `&arr+1` is really doing. – Eugene Sh. Nov 01 '17 at 13:57
  • Please review [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) and revise your question. – jhenderson2099 Nov 01 '17 at 13:59
  • What output did you get and what output do you expect? – Jabberwocky Nov 01 '17 at 14:03
  • @jhenderson2099 — it is an MCVE. It’s not very good (missing newline in output mainly), but it is an MCVE. – Jonathan Leffler Nov 01 '17 at 14:04
  • This answer might help you understand: https://stackoverflow.com/a/2528328/669576 – 001 Nov 01 '17 at 14:07
  • @JonathanLeffler - When I originally reviewed the post, the only question was in the subject. The answer to that question seemed could be researched. The question has since been reworded. Please note that with a -7 score, multiple people other than me agree that there are issues with this question. – jhenderson2099 Nov 01 '17 at 14:15
  • @jhenderson2099: Looks pretty good to me now. – Bathsheba Nov 01 '17 at 14:17
  • 1
    Generally doing things like this is a bad idea. Use `sizeof arr` to determine the size of an object, don't mess around with pointer arithmetic for that. – Jens Gustedt Nov 01 '17 at 14:40
  • @jhenderson2099: I didn't say there weren't issues with the question. I did say that it contained an MCVE. – Jonathan Leffler Nov 01 '17 at 14:57

3 Answers3

5

The behaviour of *(&arr+1) is undefined:

&arr is of type int (*)[9] due to the address of operator being applied to arr, which is of type int[9] (Acknowledgements to Klas Lindbäck for pointing out that pointer decay does not occur here.)

&arr + 1 is borderline invalid pointer arithmetic since you don't have an array of int (*)[9]. But you are allowed to set a pointer to one past a scalar, so all fine here.

But deferencing that is undefined. Don't be at all surprised if you compiler eats your cat.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 1
    It is correct, `&arr` gives you an array pointer to a single object (the array) and you are not allowed to point past that object. The rule that allows us to point one item past an array does not apply, if so the code should have been written as `(arr + 9)` which would have been well-defined. De-referencing the array pointer `*(&arr + 1)` is, arguably, de-referencing of an invalid array pointer. – Lundin Nov 01 '17 at 14:21
  • “Borderline invalid” is an interesting concept. Isn’t almost every code snippet just a small change away from invalid. Is “0” borderline invalid since using it as a divisor has undefined behavior? – Eric Postpischil Nov 01 '17 at 15:27
  • @Lundin: What do you mean the one-past rule does not apply? You do not mean `&arr+1` is undefined, do you? Do you mean that, although we can evaluate `&arr+1`, the one-past rule does not allow us to apply `*` to it? Or do you mean something else? – Eric Postpischil Nov 01 '17 at 15:30
2

First and foremost, please don't write code this way... *(&arr + 1) is undefined behavior...


Note: This answer assumes sizeof(int) = 4.

What is arr? It's an array of 9 ints

printf("%zu\n", sizeof(arr)); // 36 bytes
printf("%p\n", (void *)arr); // 0x7ffed90f48a0

What is &arr? It's a pointer to an array or ints = int (*)[9]

printf("%zu\n", sizeof(&arr)); // 8 bytes (the size of a pointer)
printf("%p\n", (void *)(&arr)); // 0x7ffed90f48a0

What is &arr + 1? Since this implies pointer arithmetic, the result is a pointer to the subsequent int (*)[9] in the memory (Notice the address gap of 0x24(36) bytes)

printf("%zu\n", sizeof(&arr + 1)); // 8 bytes (the size of a pointer)
printf("%p\n", (void *)(&arr + 1)); // 0x7ffed90f48c4

What is *(&arr + 1)? We dereference the pointer to the subsequent array &arr + 1 and get a pointer to an array of ints, just like our original arr, only that this pointer points to some invalid memory location:

printf("%zu\n", sizeof(*(&arr + 1))); // 36 bytes
printf("%p\n", (void *)(*(&arr + 1))); // 0x7ffed90f48c4

Conclusion

*(&arr + 1) - arr performs an implicit pointer arithmetic subtraction between two int arrays (pretty much the same as subtracting int *).

Since we already saw that the difference is 36 bytes, and we use int units and sizeof(int) = 4, the result is 9.

Daniel Trugman
  • 8,186
  • 20
  • 41
0

Suppose that int size is 2 bytes currently.

arr is stored at 65506 and when you do (&arr + 1) address of arr is incremented by 1 (not with an integer 1 an give you 65507), and give you next address where array ends. becuase arr is is a variable no matter of which type and how long when you increment it, it will take you to the end of variable.

Now, as i said above that int is 2 bytes long so the array of 9 will be 18 bytes long and arr+1 will give you 65506 + 18 = 65626

so, we have value of arr + 1 = 65524 and arr = 65506. i i know the anser will be 18, but as you used address of and references, it will return 9. for this you need to go deep inside dereferencing and address variables and also need to debug the stack and program for every itteration for better understanding.

By the way, you can also use another simple methods for doing this such as

   int size = sizeof(arr)/sizeof(a[0]);

Here the size of your total array, taht is 18 is devided by your first element's size. no doubt, every element present in array is of same type, same size; which is here int it will return 2 and 18 is divided by 2 and will give you 9 in simple and efficient way.

Aniket Kariya
  • 1,471
  • 2
  • 21
  • 26