c - 在刚好超过数组末尾的指针上以零长度调用 memcpy 是否合法?

标签 c pointers language-lawyer undefined-behavior

作为answered elsewhere ,使用无效或 NULL 指针调用像 memcpy 这样的函数是未定义的行为,即使 length 参数为零。在此类函数的上下文中,尤其是 memcpymemmove,刚好超过数组末尾的指针是有效指针吗?

我问这个问题是因为获取刚好超过数组末尾的指针是合法的(相对于,例如,指针超过数组末尾两个元素)但您不能取消引用它,但是ISO 9899:2011 的脚注 106 指出这样的指针指向程序的地址空间,这是根据 §7.1.4 指针有效的标准。

这种用法出现在我想将一个项目插入数组中间的代码中,要求我将所有项目移动到插入点之后:

void make_space(type *array, size_t old_length, size_t index)
{
    memmove(array + index + 1, array + index, (old_length - index) * sizeof *array);
}

如果我们要在数组的末尾插入,index等于length并且array + index + 1点刚过去数组末尾,但复制的元素个数为零。

最佳答案

将过去的结束指针传递给 memmove 的第一个参数有几个陷阱,可能导致鼻恶魔攻击。 严格来说,没有不透水的保证可以很好地定义。

(不幸的是,标准中没有太多关于“过去的最后一个元素”概念的信息。)

注意:很抱歉现在有另一个方向......

问题基本上是“结束指针之后的那个”是否是 memmove 的有效第一个函数参数如果移动了 0 个字节:

T array[length];
memmove(array + length, array + length - 1u, 0u);

有问题的要求是第一个参数的有效性。

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.

如果指针使参数有效

  1. 不在地址空间之外,
  2. 不是空指针,
  3. 不是指向常量内存的指针

如果参数类型

  1. 不是数组类型。

1。地址空间

N1570, 6.5.6, 8

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.

N1570, 6.5.6, 9

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.

尽管脚注不是规范性的——正如 Lundin 所指出的——我们在这里有一个解释,即“一个实现只需要提供一个额外的字节”。 虽然,我无法通过引用来证明,但我怀疑这是一个暗示,即标准意味着要求实现将内存包含在程序地址空间内,位于结束指针指向的位置。

2。空指针

过去的结束指针不是空指针。

3。指向常量内存

除了给出一些关于几个操作的结果的信息之外,该标准没有对过去的结束指针施加进一步的要求,并且(再次非规范;))脚注阐明它可以与另一个对象重叠。 因此,不能保证结束指针指向的内存是非常量。 自 memove 的第一个参数以来是一个指向非常量内存的指针,传递过去的结束指针不能保证是有效的并且可能是未定义的行为。

4。数组参数的有效性

第 7.21.1 章描述了字符串处理 header <string.h>第一条规定:

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.

我认为这里的标准不是很清楚“作为字符类型数组处理的对象”是指函数还是仅指宏。 如果这句话实际上暗示memove将第一个参数视为字符数组,将结束指针传递给 memmove 的行为根据 7.1.4 是未定义的行为(需要指向有效对象的指针)。

关于c - 在刚好超过数组末尾的指针上以零长度调用 memcpy 是否合法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29844298/

相关文章:

c - 在 C 中打印矩阵时获取垃圾值

c++ - 是否允许获取未定义函数的地址?

c - 使用两种不同的有符号整数二进制表示形式的程序

c - 为什么我的素数生成函数会生成一堆零?

c - 结构指针分配未分配预期值 - 链表

c - 如何扫描逗号,但逗号未分配给结构? C

c# - 在 C# struct pointer 中返回相同的地址

c++ - 为什么C++允许从外部修改一个常量对象的指针成员变量的内存?

c++ - C++17 中 protected 构造函数的规则改变了吗?

C - 指向动态结构数组的指针