Dereferencing One Past the End Pointer to Array Type

Dereferencing one past the end pointer to array type

This is CWG 232. That issue might seem like it's mainly about dereferencing a null pointer but it's fundamentally about what it means to simply dereference something that doesn't point to an object. There is no explicit language rule about this case.

One of the examples in the issue is:

Similarly, dereferencing a pointer to the end of an array should be allowed as long as the value is not used:

char a[10];
char *b = &a[10]; // equivalent to "char *b = &*(a+10);"

Both cases come up often enough in real code that they should be allowed.

This is basically the same thing as OP (the a[10] part of the above expression), except using char instead of an array type.

Common wisdom is that it's undefined behavior to dereference a one-past-the-end pointer. However, does this hold true for pointers to array types?

There is no difference in the rules based on what kind of pointer it is. my_past_end is a past-the-end pointer, so whether it's UB to dereference it or not is not a function of the fact that it points to an array as opposed to any other kind of type.


While the type of is_this_valid an int* which gets initialized from a int(&)[3] (array-to-pointer decay), and thus nothing here actually reads from memory - that is immaterial to the way the language rules work. my_past_end is a pointer whose value is past the end of an object, and that's the only thing that matters.

Why dereferencing is not required in pointer array?

we should use the * operator before arr[i]

No, that'd be an error.

From cppreference:

The built-in subscript expression E1[E2] is exactly identical to the expression *(E1 + E2)except evaluation order (since C++17), that is, the pointer operand (which may be a result of array-to-pointer conversion, and which must point to an element of some array or one past the end) is adjusted to point to another element of the same array, following the rules of pointer arithmetics, and is then dereferenced.

so operator[] takes care of dereferencing.

Is the one-past-the-end pointer of a non-array type a valid concept in C++?

No, it is legal. 5.7(4) - one paragraph before your quote - says: "For the purposes of these operators, a pointer to a nonarray object behaves the same as a pointer to the
first element of an array of length one with the type of the object as its element type."

Are non dereferenced iterators past the one past-the-end iterator of an array undefined behavior?

Yes, your program has undefined behaviour if you form such a pointer.

That's because the only way you can do so is to increment a valid pointer past the bounds of the object it points inside, and that is an undefined operation.

[C++14: 5.7/5]: When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integral expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i + n-th and i − n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

An uninitialised pointer is not the same thing because you never did anything to "get" that pointer, other than declaring it (which is obviously valid). But you can't even evaluate it (not dereference — evaluate) without imbuing your program with undefined behaviour. Not until you've assigned it a valid value.

As a sidenote, I would not call these "past-the-end" iterators/pointers, a term in C++ which specifically means the "one past-the-end" iterator/pointer, which is valid (e.g. cend(foo) itself). You're waaaay past the end. ;)

Is comparing to a pointer one element past the end of an array well-defined?

Comparing to a pointer one step past the end of an array is well defined. However, your pointer and pointer2 examples are undefined, even if you do literally nothing with those pointers.

A pointer may point to one element past the end of the array. That pointer may not be dereferenced (otherwise that would be undefined behavior) but it can be compared to another pointer within the array.

Section 6.5.6 of the C standard says the following regarding pointer addition (emphasis added):

8 If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array
object
, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined
. 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.

Section 6.5.8 says the following regarding pointer comparisons (emphasis added):

5 When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. If two
pointers to object types both point to the same object, or both point
one past the last element of the same array object, they compare
equal.
If the objects pointed to are members of the same aggregate
object, pointers to structure members declared later compare greater
than pointers to members declared earlier in the structure, and
pointers to array elements with larger subscript values compare
greater than pointers to elements of the same array with lower
subscript values. All pointers to members of the same union object
compare equal. If the expression P points to an element of an array
object and the expression Q points to the last element of the same
array object, the pointer expression Q+1 compares greater than P. In
all other cases, the behavior is undefined.

In the case of pointer1, it starts out pointing to NULL. Incrementing this pointer invokes undefined behavior because it don't point to a valid object.

For pointer2, it is increased by 4, putting it two elements past the end of the array, not one, so this is again undefined behavior. Had it been increased by 3, the behavior would be well defined.

Is it safe to keep a pointer out-of-bounds without dereferencing it?

Moving pointer to one element past the last element is allowed, but moving further or moving before the first element is not allowed.

Quote from N1570 6.5.6 Additive operators (point 8):

When an expression that has integer type is added to or subtracted from a pointer, the
result has the type of the pointer operand. If the pointer operand points to an element of
an array object, and the array is large enough, the result points to an element offset from
the original element such that the difference of the subscripts of the resulting and original
array elements equals the integer expression. In other words, if the expression P points to
the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and
(P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of
the array object, provided they exist. Moreover, if the expression P points to the last
element of an array object, the expression (P)+1 points one past the last element of the
array object, and if the expression Q points one past the last element of an array object,
the expression (Q)-1 points to the last element of the array object. If both the pointer
operand and the result point to elements of the same array object, or one past the last
element of the array object, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined. 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.

Pointer arithmetic with one past array element and then dereference, what will be the outcome?

TL;DR: Don't know, can't say.

To elaborate, in your code

  printf("\n*(meatBalls+5) \t\t %d \n", *(meatBalls+5));

causes undefined behavior as you try to access out of bound memory.

Please note the emphasis on access. The pointer arithmetic in this case is well-defined (one past the last element), attempt to access in what causes the UB.

Quoting C11, chapter §6.5.6 (emphasis mine)

[...] if the expression P points to the last
element of an array object, the expression (P)+1 points one past the last element of the
array object, and if the expression Q points one past the last element of an array object,
the expression (Q)-1 points to the last element of the array object. If both the pointer
operand and the result point to elements of the same array object, or one past the last
element of the array object, the evaluation shall not produce an overflow;
otherwise, the
behavior is undefined. 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.

That said, %p expects its argument to be a void* and since printf() is a variadic function, no default argument promotion takes place. So, you need to cast all the arguments to %p to (void *)



Related Topics



Leave a reply



Submit