As answered elsewhere, calling functions like memcpy
with invalid or NULL
pointers is undefined behaviour, even if the length argument is zero. In the context of such a function, especially memcpy
and memmove
, is a pointer just past the end of the array a valid pointer?
I'm asking this question because a pointer just past the end of an array is legal to obtain (as opposed to, e.g. a pointer two elements past the end of an array) but you are not allowed to dereference it, yet footnote 106 of ISO 9899:2011 indicates that such a pointer points to into the address space of the program, a criterion required for a pointer to be valid according to §7.1.4.
Such usage occurs in code where I want to insert an item into the middle of an array, requiring me to move all items after the insertion point:
void make_space(type *array, size_t old_length, size_t index)
{
memmove(array + index + 1, array + index, (old_length - index) * sizeof *array);
}
If we want to insert at the end of the array, index
is equal to length
and array + index + 1
points just past the end of the array, but the number of copied elements is zero.
If we look at the C99 standard, there is this:
7.21.1.p2
There is no explicit statement in the description of
memcpy
in 7.21.2.17.1.4.p1
Emphasis added. It seems the pointers have to point to valid locations (in the sense of dereferencing), and the paragraphs about pointer arithmetic allowing to point to the end + 1 do not apply here.
There is the question if the arguments to
memcpy
are arrays or not. Of course they are not declared as arrays, but7.21.1.p1 says
and
memcpy
is in string.h.So I would assume
memcpy
does treat the arguments as arrays of characters. Because the macro mentioned isNULL
, the "useful for..." part of the sentence clearly applies to the functions.The memory, pointer to one past the last element points to, of an array object or an object cannot represent values, since it cannot be dereferenced ( 6.5.6 Additive operators, paragraph 8 ).
Pointers passed to memcpy must point to an object.
sizeof operator doesn't count the one-past element as the object, since it doesn't count towards the size of the object. Yet it clearly gives the size of the entire object.
I argue that the one past pointer to an array object or an object, both of which are otherwise allowed to point to, does not represent an object.
p
is defined, but it does not point to an object since it cannot be dereferenced, the memory it points to cannot represent a value, and sizeof doesn't count that memory as a part of the object. Memcpy requires a pointer to an object.Therefore the passing one past pointer to memcpy causes undefined behavior.
Update:
This part also support the conclusion:
This implies that pointer to an object if incremented to one past an object, can point to a different object. In that case, it certainly cannot point to the object it pointed to originally, showing that pointer one past an object doesn't point to an object.
Passing the past the end pointer to the first argument of
memmove
has several pitfalls, probably resulting in a nasal demon attack. Strictly speaking, there is no impermeable guarantee for that to be well defined.(Unfortunatelly, there is not much information about the "past the last element" conecpt in the standard.)
Note: Sorry about having the other direction now...
The question basicially is whether the "one past the end pointer" is a valid first function argument for
memmove
if 0 bytes are moved:The requirement in question is the validity of the first argument.
N1570, 7.1.4, 1
Making the argument valid if the pointer
and if the argument type
1. Address space
N1570, 6.5.6, 8
N1570, 6.5.6, 9
Eventhough the footnote is not normative -as pointed out by Lundin- we have an explanation here that "an implementation need only provide one extra byte". Although, I can't proove by quoting I suspect that this is a hint that the standard means to require the implementation to included memory inside of the programs address space at the location pointed to by the past the end pointer.
2. Null Pointer
The past the end pointer is not a null pointer.
3. Pointing to const memory
The standard imposes no further requirements on the past the end pointer other than giving some information about the result of several operations and the (again non-normaltive ;)) footnote clarifies that it can overlap with another object. Thus, there is no guarantee that the memory the past the end pointer points at is non constant. Since the first argument of
memove
is a pointer to non-constant memory, passing the past the end pointer is not guaranteed to be valid and potentially undefined behaviour.4. Validity of array arguments
Chapter 7.21.1 describes the string handling header
<string.h>
and the first clause states:I don't think that the standard is very clear here whether the "objects treated as arrays of character type" refers to the functions or to the macro only. If this sentence actually implies that
memove
treats the first argument as an array of characters, the behaviour of passing the past the end pointer tomemmove
is undefined behaviour as per 7.1.4 (which requires a pointer to a valid object).