&arr[size] 有效吗?

Is &arr[size] valid?

本文关键字:有效 size arr      更新时间:2023-10-16

假设我有一个函数,调用如下:

void mysort(int *arr, std::size_t size)
{
  std::sort(&arr[0], &arr[size]);
}
int main()
{
  int a[] = { 42, 314 };
  mysort(a, 2);
}

我的问题是:mysort守则(更具体地说,&arr[size](是否有定义的行为?

我知道如果用arr + size替换它是完全有效的;指针算法允许通常指向终点。但是,我的问题专门针对&[]的使用。

根据 C++11 5.2.1/1,arr[size] 相当于 *(arr + size)

引用 5.3.1/1,一元*的规则:

一元*运算符执行间接寻址:应用它的表达式应是指向 对象类型,或指向函数类型的指针,结果是引用对象或函数的左值表达式指向的。如果表达式的类型是"指向T的指针",则结果的类型是"T"。 [ 注意:指向不完整类型(cv void 除外(的指针可以取消引用。由此获得的左值 可以以有限的方式使用(例如,初始化引用(;此左值不得转换为 PR值,见 4.1。—尾注 ]

最后,5.3.1/3给出了&的规则:

一元运算符的结果&是指向其操作数的指针。操作数应为左值...如果类型 表达式是T,结果的类型是"指向T的指针",并且是一个 prvalue 是指定对象的地址 (1.7( 或指向指定函数的指针。

(强调和省略号我的(。

我对此下不了定决心。我确信在arr[size]上强制左值到右值的转换将是未定义的。但是代码中没有发生这样的转换。 arr + size不指向对象;但是,虽然上面的段落谈到了对象,但它们似乎从未明确指出对象实际存在于该位置的必要性(与例如 4.1/1 中的左值到右值转换不同(。

所以,问题在于:mysort,它的称呼方式是否有效?

(请注意,我引用了上面的C++11,但如果在以后的标准/草案中更明确地处理这一点,我会对此非常满意(。

它无效。您在问题中加粗"结果是一个左值,指的是表达式指向的对象或函数"。这正是问题所在。 array + size是不指向对象的有效指针值。因此,您关于*(array + size)的报价没有指定结果所指的内容,这意味着&*(array + size)不需要给出与array + size相同的值。

C中,这被认为是一个缺陷并被修复,因此规范现在在&*ptr中说,&*都没有被评估。C++尚未收到固定的措辞。这是一个非常古老的仍然活跃的DR的主题:DR #232。目的是它是有效的,就像它在 C 中一样,但标准没有这么说。

在正常C++数组的上下文中,是的。 形成数组的"一个过去结束"元素的地址是合法的。 但是,读取或写入它所指向的内容是不合法的(毕竟,那里没有实际元素(。 因此,当您执行&arr[size]时,arr[size]形成了您可能认为的对"一过完"元素的引用,但尚未尝试实际访问该元素。 然后&会得到该元素的地址。 由于没有任何东西试图真正遵循该指针,因此没有发生任何不好的事情。

这并非偶然,这使得数组中的指针表现得像迭代器。 因此&a[0]本质上.begin()数组,而&a[size](其中大小是数组中元素的数量(本质上是.end()的。 (另请参阅std::array

,最终更明确(

编辑:呃,我可能不得不收回这个答案。 虽然它可能适用于大多数情况,但如果存储在数组中的类型具有重写operator&则当您执行&a[size]时,operator&方法可能会尝试在没有实例a[size]访问该类型的实例的成员。

假设size是实际的数组大小,您将一个指向过去结束元素的指针传递给std::sort()

所以,据我了解,问题归结为:这个指针等同于arr.end()吗?

毫无疑问,对于每个现有的编译器来说都是如此,因为数组迭代器确实是普通的旧指针,因此&arr[size]arr.end()的明显选择。

但是,我怀疑对普通旧数组迭代器的实际实现是否有特定要求。

因此,为了论证起见,您可以想象一个编译器在实际地址之外使用"过去结束"位在内部实现普通的旧数组迭代器,如果它检测到迭代器和通过指针算术获得的地址之间存在任何可协商的不一致则会反常地将您的胡子涂成粉红色。这个怪异的编译器会导致许多现有的C++代码崩溃而不会实际违反规范,这可能只是值得设计它的努力......

如果我们承认arr[i]只是*(arr + i)的简写,我们可以将&arr[size]重写为&*(arr + size)。因此,我们正在取消引用指向 past-the-end 元素的指针,这会导致未定义的行为。正如您正确所说,arr + size反而是合法的,因为不会发生取消引用操作。

巧合的是,这也在斯捷潘诺夫的笔记(第11页(中作为测验呈现。

只要

大小不大于实际数组的大小(以数组元素为单位(,它就完全可以很好地定义。

因此,如果 main (( 称为 mysort (a, 100(,&arr [size] 已经是未定义的行为(但很可能未检测到,但 std::sort 显然也会严重出错(。