我们说我有一个函数,就像这样:
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
取代,那将完全有效;指针算法允许正常指向过去。但是,我的问题是关于&
和[]
的使用。
Per C ++ 11 5.2.1 / 1,arr[size]
相当于*(arr + size)
。
引用5.3.1 / 1,一元*
的规则:
一元
*
运算符执行间接:应用它的表达式应该是指向 对象类型,或指向函数类型的指针,结果是引用对象或函数的左值 表达式指向。如果表达式的类型是“指向T
的指针”,则结果的类型为“T
”。 [注意:指向不完整类型的指针( cvvoid
除外)可以取消引用。由此获得的左值 可以以有限的方式使用(例如,初始化参考);此左值不得转换为 prvalue,见4.1。 -end note ]
最后,5.3.1 / 3给出了&
的规则:
一元
&
运算符的结果是指向其操作数的指针。操作数应为左值...如果类型为 表达式为T
,结果类型为“指向T
的指针”,是一个prvalue,它是指定对象的地址(1.7)或指向指定对象的指针功能
(强调和省略我的。)
我无法对此作出决定。我确信在arr[size]
上强制进行左值到右值的转换将是未定义的。但是代码中没有发生这样的转换。 arr + size
没有指向一个对象;但是,虽然上面的段落谈论了对象,但它们似乎从未明确地指出对象实际存在于该位置的必要性(与4.1 / 1中的左值到右值转换不同)。
所以,问题是:是mysort
,它被调用的方式,是否有效?
(请注意,我在上面引用了C ++ 11,但如果在以后的标准/草案中更明确地处理这个问题,我会非常满意)。
答案 0 :(得分:9)
它无效。你加粗了#34;结果是一个左值,指的是表达式指向的对象或函数。在你的问题。这正是问题所在。 array + size
是一个有效的指针值,不指向对象。因此,您对*(array + size)
的引用未指明结果的引用,这意味着&*(array + size)
无需提供与array + size
相同的值。
在C中,这被认为是一个缺陷并已修复,因此现在在&*ptr
中说明了规范,&
和*
都没有得到评估。 C ++还没有收到固定的措辞。它是一个非常古老的仍然活跃的DR的主题:DR #232。意图是它有效,就像它在C中一样,但标准并没有这样说。
答案 1 :(得分:1)
在普通C ++数组的上下文中,是的。形成数组的一个接一个元素的地址是合法的。然而,读取或写入它指向的内容是不合法的(毕竟,那里没有实际的元素)。因此,当您执行&arr[size]
时,arr[size]
形成了您可能认为的对一个过去结束元素的引用,但尚未尝试实际访问该元素。然后&
会获取该元素的地址。由于没有试图真正遵循该指针,所以没有发生任何不好的事情。
这不是偶然的,这使得指向数组的指针就像迭代器一样。因此,&a[0]
在数组上基本上是.begin()
,而&a[size]
(其中size是数组中元素的数量)基本上是.end()
。 (另请参阅std::array
,其结果更明确)
编辑:呃,我可能要撤回这个答案。虽然它可能适用于大多数情况,但如果存储在数组中的类型具有被覆盖的operator&
,那么当您执行&a[size]
时,operator&
方法可能会尝试访问该实例的成员a[size]
处的类型,其中没有实例。
答案 2 :(得分:0)
假设size
是实际的数组大小,您将指向past-the-end元素的指针传递给std::sort()
。
所以,据我所知,问题归结为:这个指针是否等同于arr.end()
?
毫无疑问,对于每个现有的编译器都是如此,因为数组迭代器确实是普通的旧指针,因此&arr[size]
是arr.end()
的明显选择。
但是,我怀疑对普通旧数组迭代器的实际实现有一个特定的要求。
所以,为了论证,你可以想象一个使用"过去结束的编译器"如果在迭代器和通过指针算术获得的地址之间发现任何可见的不一致,那么在内部实现普通旧数组迭代器的实际地址和会不正常地涂上你的小胡子粉红色。 这个奇特的编译器会导致很多现有的C ++代码崩溃而不会实际违反规范,这可能值得设计它...
答案 3 :(得分:0)
如果我们承认arr[i]
只是*(arr + i)
的简写,我们可以将&arr[size]
重写为&*(arr + size)
。因此,我们正在取消引用指向past-the-end元素的指针,这会导致未定义的行为。正如您所说,arr + size
将是合法的,因为不会发生解除引用操作。
巧合的是,这也是Stepanov的notes(第11页)中的测验。
答案 4 :(得分:-5)
只要尺寸不大于实际阵列的大小(以数组元素为单位),它就非常精细且定义良好。
因此,如果main()调用mysort(a,100),& arr [size]已经是未定义的行为(但很可能未检测到,但std :: sort显然也会出错)。