I have read online that memmove
is expected to perform no action if the number of bytes to copy is 0. However what I want to know is if it is expected that the source and destination pointers will not be read in that case
Below is a simplified version of some of my code, the section I am interested in is shiftLeft
:
#include <array>
#include <cstring>
#include <iostream>
class Foo final {
unsigned just = 0;
unsigned some = 0;
unsigned primitives = 0;
};
template <unsigned Len>
class Bar final {
unsigned depth = 0;
std::array<Foo, Len> arr;
public:
Bar() = default;
// Just an example
void addFoo() {
arr[depth] = Foo();
depth++;
}
void shiftLeft(unsigned index) {
// This is what my question focuses on
// If depth is 10 and index is 9 then index + 1 is out of bounds
// However depth - index - 1 would be 0 then
std::memmove(
&arr[index],
&arr[index + 1],
(depth - index - 1) * sizeof(Foo)
);
depth--;
}
};
int main() {
Bar<10> bar;
for (unsigned i = 0; i < 10; ++i)
bar.addFoo();
bar.shiftLeft(9);
return 0;
}
When Len
is 10
, depth
is 10
, and index
is 9
then index + 1
would read out of bounds. However also in that case depth - index - 1
is 0
which should mean memmove
would perform no action. Is this code safe or not?
The memmove
function will copy n
bytes. If n
is zero, it will do nothing.
The only possible issue is with this, where index
is already at the maximum value for array elements:
&arr[index + 1]
However, you are permitted to refer to array elements (in terms of having a pointer point to them) within the array or the hypothetical element just beyond the end of the array.
You may not dereference the latter but you're not doing that here. In other words, while arr[index + 1]
on its own would attempt a dereference and therefore be invalid, evaluating the address of it is fine.
This is covered, albeit tangentially, in C++20 [expr.add]
:
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 expression
P
points to elementx[i]
of an array objectx
withn
elements, the expressionsP + J
andJ + P
(whereJ
has the valuej
) point to the (possibly-hypothetical) elementx[i + j] if 0 ≤ i + j ≤ n
; otherwise, the behavior is undefined.
Note the if 0 ≤ i + j ≤ n
clause, particularly the final ≤
. For an array int x[10]
, the expression &(x[10])
is valid.
It's also covered in [basic.compound]
(my emphasis):
A value of a pointer type that is a pointer to or past the end of an object represents the address of the first byte in memory occupied by the object or the first byte in memory after the end of the storage occupied by the object, respectively.