ptrdiff_t可以表示指向同一数组对象元素的指针的所有减法吗?

Can ptrdiff_t represent all subtractions of pointers to elements of the same array object?

本文关键字:指针 元素 对象 数组 表示 ptrdiff      更新时间:2023-10-16

对于i指针的减法,并j到同一数组对象的元素,[expr.add#5] 中的注释如下:

[注意如果值i−j不在类型std​::​ptrdiff_­t的可表示值范围内,则行为未定义。

但是给定[support.types.layout#2],它指出(强调我的):

    类型
  1. ptrdiff_­t是实现定义的有符号整数类型,可以在数组对象中保存两个下标的差异,如 [expr.add] 中所述。

i-j的结果是否有可能不在ptrdiff_t的可表示值范围内?

PS:如果我的问题是由于我对英语的理解不佳引起的,我深表歉意。

编辑:相关:为什么数组的最大大小"太大"?

i-j的结果是否有可能在ptrdiff_t的可表示值范围内?

是的,但这不太可能。

事实上,除了在[expr.add]中定义了关于指针减法和ptrdiff_t的正确规则外,[support.types.layout]/2并没有说太多。因此,让我们看看本节。

[expr.add]/5

当减去指向同一数组对象的元素的两个指针时,结果的类型是实现定义的有符号整数类型;此类型应与在<cstddef>标头中定义为std​::​ptrdiff_­t的类型相同。

首先,请注意,不考虑ij是不同数组的下标索引的情况。这允许将i-j视为P-Q其中P是指向下标i处数组元素的指针,Q是指向下标j同一数组元素的指针。实际上,减去指向不同数组元素的两个指针是未定义的行为

[expr.add]/5

如果表达式PQ分别指向同一数组对象的元素x[i]x[j]x,则表达式P - Q的值为i−j;否则,行为是未定义的

作为结论,使用前面定义的符号,i-jP-Q被定义为具有相同的值,后者的类型为std::ptrdiff_t。但是没有提到这种类型保持这种值的可能性。但是,这个问题可以在std::numeric_limits的帮助下得到回答;特别是,可以检测数组some_array是否太大std::ptrdiff_t无法容纳所有索引差异:

static_assert(std::numeric_limits<std::ptrdiff_t>::max() > sizeof(some_array)/sizeof(some_array[0]),
"some_array is too big, subtracting its first and one-past-the-end element indexes "
"or pointers would lead to undefined behavior as per [expr.add]/5."
);

现在,在通常的目标上,这通常不会像sizeof(std::ptrdiff_t) == sizeof(void*)那样发生;这意味着数组需要非常大才能ptrdiff_t溢出。但不能保证。

我认为这是措辞的错误。

[expr.add] 中的规则继承自 C 标准中指针减法的相同规则。在 C 标准中,ptrdiff_t不需要在数组对象中保存两个下标的任何差异。

[support.types.layout] 中的规则来自核心语言问题 1122。它增加了std::size_tstd::ptrdiff_t的直接定义,这应该解决循环定义的问题。我认为没有任何理由(至少在任何官方文档中都没有提到)使数组对象中std::ptrdiff_t两个下标的任何差异。我想它只是使用不正确的定义来解决循环定义问题。

作为另一个证据,[diff.library]没有提到C++中的std::ptrdiff_t和C中的ptrdiff_t之间的任何区别。既然在C中ptrdiff_t没有这样的约束,那么在C++std::ptrdiff_t中也不应该有这样的约束。