As answered elsewhere, calling functions like memcpy
with invalid or NULL
pointers is undefined behaviour, even if the length argument is zero. In the
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:
T array[length];
memmove(array + length, array + length - 1u, 0u);
The requirement in question is the validity of the first argument.
N1570, 7.1.4, 1
If a function argument is described as being an array, the pointer actually passed to the function shall have a value such that all address computations and accesses to objects (that would be valid if the pointer did point to the first element of such an array) are in fact valid.
If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined.
Making the argument valid if the pointer
and if the argument type
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.
Moreover, if the expression P points either to an element of an array object or one past the last element of an array object, and the expression Q points to the last element of the same array object, the expression ((Q)+1)-(P) has the same value as ((Q)-(P))+1 and as -((P)-((Q)+1)), and has the value zero if the expression P points one past the last element of the array object, even though the expression (Q)+1 does not point to an element of the array object.106
106 Another way to approach pointer arithmetic is first to convert the pointer(s) to character pointer(s): In this scheme the integer expression added to or subtracted from the converted pointer is first multiplied by the size of the object originally pointed to, and the resulting pointer is converted back to the original type. For pointer subtraction, the result of the difference between the character pointers is similarly divided by the size of the object originally pointed to.
When viewed in this way, an implementation need only provide one extra byte (which may overlap another object in the program) just after the end of the object in order to satisfy the "one past the last element" requirements.
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.
The past the end pointer is not a null pointer.
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.
Chapter 7.21.1 describes the string handling header
and the first clause states:
The header declares one type and several functions, and defines one macro useful for manipulating arrays of character type and other objects treated as arrays of character type.
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 to memmove
is undefined behaviour as per 7.1.4 (which requires a pointer to a valid object).