7

I do not believe this a duplicate (see below).


I found the question that is nearly a precise duplicate, but I think the answers failed to analyze the loophole. See: Is `*((*(&array + 1)) - 1)` safe to use to get the last element of an automatic array?


I know the usual method is to compute sizeof(array)/sizeof(*array) to get the number of elements in the array. (And it has been pointed out to me in comments that C++ has std::size(array).)

So, I reasoned that number could be calculated with pointer math if I could get to the address of one past the last element. But, then it occurred to me that I could:

&array + 1

But, the type of this value is pointer to array, so to allow the math to work, I would need to dereference this value.

const auto N = *(&array + 1) - array;

Is there an allowance in C and C++ that allows this dereference to be legal?

I know that you are allowed to point to one past the last element of an array, but you are not allowed to dereference it. However, this dereference yields an array object, which immediately decays to a pointer to its first element.

I could use a reinterpret_cast to avoid using the dereference operator, but I was wondering if the the C and/or C++ languages would allow the dereference in this case.


I missed a similar question, but this question is subtly different. I already understand how the code works, and I understand that there is normally a problem with dereferencing the pointer past the last element of the array. I am asking if the fact that array objects decay to a pointer when used in an expression allows for an exception. I think it might, because dereferencing a pointer to an object would normally result in the object value itself to be used in the expression (which would be a problem if the object does not exist).

jxh
  • 69,070
  • 8
  • 110
  • 193
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/202044/discussion-on-question-by-jxh-pointer-math-to-get-the-length-of-an-array). – Samuel Liew Nov 08 '19 at 10:43

2 Answers2

4

Nope. Doing this is undefined behavior:

Be aware, the expression *(&a + 1) results in undefined behavior:

...

If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

C 2011 Online Draft, 6.5.6/9

So attempting to dereference something out of bounds will result in undefined behavior. No exceptions.


Edit: I might be wrong. There is a another source on this issue saying that you are able to dereference it (emphasis mine):

[Note: for instance, the address one past the end of an array (5.7) would be considered to point to an unrelated object of the array’s element type that might be located at that address. —end note ]

Which seems to me to imply that yes, you can legally dereference it, but the result of reading or writing to the location is unspecified.

So, because it is the 1 past pointer, (according to this author) it is okay to dereference it, just not read or write. In this case, this won't affect you since you are using it for its size properties.

Though, keep in mind, anything more than 1 after the end (as opposed to the beginning) and this won't work.


Actually, this is disputed, possibly even by the committee itself. Though I should not that the question linked here is slightly different than this one.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
  • I haven't taken any action on any answer yet (other than this comment). I understand this position. I am just unsure if it is actually true. – jxh Nov 08 '19 at 01:04
  • I don't understand. Why wouldn't this quote apply? –  Nov 08 '19 at 01:07
  • 1
    If I was going to pretend to be a language lawyer, I might argue that the ***that is evaluated*** part might be a loophole when dereferencing a pointer to an array. – jxh Nov 08 '19 at 01:09
  • @jxh I might be wrong, and you right. What to do you think of my edit? –  Nov 08 '19 at 02:17
  • I will have to think more about what question I want to ask to follow up. Thanks for your answer. – jxh Nov 08 '19 at 05:21
  • It seems you did a better job of finding an answer than I did. But you're welcome anyhow. –  Nov 08 '19 at 05:27
1

Yes, there's something both C and C++ which makes this idea legal.

A pointer one after an object (including array objects) can be used for pointer comparison and arithmetic, even though it cannot be dereferenced. What you want to do is create a pointer to the int[5] after array, convert that pointer to an int* (which is now points after array[N-1], and then subtract &array[0].

Your "dereference" implementation is a problem, though. There's no actual second array after array. Your dereference is not necessary at all; you just use it to trigger an implicit conversion from int[N] to int*. But you can just use an explicit covnersion instead, (int*)(&array+1)

[C++17 wording, emphasis mine]

For purposes of pointer arithmetic (5.7 expr.add) and comparison (5.9 expr.rel, 5.10 expr.eq), a pointer past the end of the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical element x[n]

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 1
    The "cast" solution was discussed and dismissed in the last paragraph of the question. – user3386109 Nov 08 '19 at 00:26
  • You are correct the dereference is meant to trigger an implicit conversion. This is why I think it may allow for an exception, since the dereference of a pointer to array does not trigger in an object value to be evaluated, but rather conversion of type of the same pointer value. – jxh Nov 08 '19 at 01:23
  • _But you can just use an explicit covnersion instead_ How should it help? `(int*)(&array+1) - array` has UB. – Language Lawyer Nov 08 '19 at 01:56
  • @LanguageLawyer: It does not have undefined behavior, presuming `array` is an array of `int` or is suitably aligned for `int *`. C 2018 6.3.2.3 7 says a pointer to an object type may be converted to a pointer to a different object type. If the result is not suitably aligned, the behavior is undefined. Otherwise, when converted back, it shall compare equal to the original. However, that is all it says, so the use to which it is put in this answer (`(int *)(&array+1) - &array[0]`) is not specified to work. – Eric Postpischil Nov 08 '19 at 02:40
  • @EricPostpischil The answerer cites the C++17 Standard and `(int*)(&array+1) - array` has UB in C++17. – Language Lawyer Nov 08 '19 at 02:45
  • @LanguageLawyer: Okay, C versus C++. Both the question and this answer mention both. – Eric Postpischil Nov 08 '19 at 02:46
  • @EricPostpischil As you rightly noted, the behavior of `(int*)(&array+1) - array` is underspecified in C. So it is not really "versus". – Language Lawyer Nov 08 '19 at 02:54
  • Thanks for your answer. I want to explore more about why exactly the pointer arithmetic expression is underspecified/undefined. – jxh Nov 08 '19 at 05:22