33

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

[ <em>注意:如果值i-j不在 type 的可表示值范围内std​::​ptrdiff_­t,则行为未定义。— <em>结束注释]

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

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

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

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

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

4

2 回答 2

5

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

是的,但不太可能。

事实上,[support.types.layout]/2除了关于指针减法的正确规则并ptrdiff_t[expr.add]. 那么让我们看看这个部分。

[expr.add]/5

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

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

[expr.add]/5

如果表达式PQ分别指向同一数组对象的元素x[i]和,则表达式具有值 ;否则,行为是 undefinedx[j]xP - Qi−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才能溢出。但不能保证。

于 2018-03-20T09:30:32.530 回答
1

我认为这是措辞的错误。

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

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

作为另一个证据,[diff.library]std::ptrdiff_t没有提到C++ 和 C 中的任何区别ptrdiff_t。由于 Cptrdiff_t中没有这样的约束,所以 C++ 中std::ptrdiff_t也不应该有这样的约束。

于 2018-03-23T15:45:24.200 回答