1

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?

asimes
  • 5,749
  • 5
  • 39
  • 76
  • Even if it's safe (as @paxdiablo explained in his answer), it's not correct: nothing will be moved, but `depth` will be decremented. – Vlad Feinstein Nov 13 '20 at 00:10
  • @VladFeinstein, No, it is correct. When the last element is removed nothing needs to shift (no `memmove`) but the number of elements needs to decrease (`depth--`) – asimes Nov 13 '20 at 00:12

1 Answers1

4

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 element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[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.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Is it also safe to assume `memmove` will not read `arr[index + 1]`? – asimes Nov 12 '20 at 23:58
  • @asimes: there is no *reason* for this read to happen: from the referenced ISO C section: "the `memmove` function copies `n` characters from the object pointed to by `s2` into the object pointed to by `s1`". There's no specific *guarantee* in the standard that it won't but that's no different to saying there's no guarantee that it won't try to dereference the constant pointer `0xdeadbeef` :-) If it *does* do it, you can assume it knows that it's safe to do so. I think it's reasonably safe to assume that library functions will do what they state they do, and no more. – paxdiablo Nov 13 '20 at 00:16
  • @asimes `memmove()` may read `arr[index + 1]` - it might like to read 8 bytes at a time. It may read `arr[index - 42]`. It can do things safely that user code can not. Why do you care if it reads `arr[index + 1]`? – chux - Reinstate Monica Nov 13 '20 at 00:16
  • @chux-ReinstateMonica, I care because reading that would be undefined behavior – asimes Nov 13 '20 at 00:18
  • 2
    @asimes: UB dictates what *your* code is permitted to do, the code within an implementation is not restricted by that. For example, if it always gives you arrays rounded up to a multiple of eight bytes, it knows it's safe to copy them in chunks of eight and can act accordingly. – paxdiablo Nov 13 '20 at 00:21
  • 1
    For the above reason trying to learn to program by studying a Standard library implementation can lead you down many bad paths. The folks writing the library know exactly how otherwise batsmurf crazy code is going to behave with their compiler on the targeted hardware and to get every last drop of performance out of the code they WILL take shortcuts through undefined territory. When you really know the rules you know when you can safely break them. – user4581301 Nov 13 '20 at 00:39
  • @user4581301: My new favourite word is now "batsmurf" :-) – paxdiablo Nov 13 '20 at 00:58
  • Thanks! My profanity options are kind of limited here so I make do with what I can get. – user4581301 Nov 13 '20 at 01:54