memmove 是否复制 0 个字节但引用越界安全

Is memmove copying 0 bytes but referencing out of bounds safe

我在网上看到,如果要复制的字节数为 0,memmove 预计不会执行任何操作。但是我想知道的是,如果预计源指针和目标指针不会在这种情况下被阅读

下面是我的一些代码的简化版本,我感兴趣的部分是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;
}

Len10depth10index9 时,index + 1 将读取出界。然而在那种情况下 depth - index - 10 这应该意味着 memmove 将不执行任何操作。这段代码安全吗?

memmove 函数将复制 n 个字节。如果 n 为零,它将不执行任何操作。

唯一的可能问题在于此,其中index已经是数组元素的最大值:

&arr[index + 1]

但是,您可以引用数组 中的数组元素(根据指向它们的指针)或数组末尾后面的假设元素.

您可能不会取消引用 后者,但您不会在这里这样做。换句话说,虽然 arr[index + 1] 本身会尝试取消引用并因此无效,但评估它的 地址 是可以的。

这在 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.

注意 if 0 ≤ i + j ≤ n 子句,尤其是最后的 。对于数组 int x[10],表达式 &(x[10]) 有效。

[basic.compound]中也有介绍(我的重点):

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.