&arr[size] 有效吗?
Is &arr[size] valid?
假设我有一个这样调用的函数:
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,一元规则*
:
The unary *
operator performs indirection: the expression to which it is applied shall be a pointer to an
object type, or a pointer to a function type and the result is an lvalue referring to the object or function
to which the expression points. If the type of the expression is “pointer to T
,” the type of the result is “T
.”
[ Note: a pointer to an incomplete type (other than cv void
) can be dereferenced. The lvalue thus obtained
can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a
prvalue, see 4.1. —end note ]
最后,5.3.1/3给出了&
的规则:
The result of the unary &
operator is a pointer to its operand. The operand shall be an lvalue ... if the type of
the expression is T
, the result has type “pointer to T
” and is a prvalue that is the address of the designated object (1.7) or a pointer to the designated function.
(强调和省略我的)。
我不能完全下定决心。我确信在 arr[size]
上强制进行左值到右值的转换是未定义的。但是代码中没有发生这种转换。 arr + size
没有指向对象;但是虽然上面的段落讨论了对象,但它们似乎从未明确指出对象实际存在于该位置的必要性(不像 4.1/1 中的左值到右值转换)。
所以,问题是:mysort
,它的调用方式是否有效?
(请注意,我在上面引用了 C++11,但如果在后面 standard/draft 中更明确地处理这个问题,我会非常高兴)。
只要大小不大于实际数组的大小(以数组元素为单位),它就非常好并且定义明确。
所以如果 main() 调用了 mysort(a, 100),&arr [size] 就已经是未定义的行为了(但很可能未被发现,但 std::sort 显然也会出错)。
在普通 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()
的明显选择。
但是,我怀疑对于普通旧数组迭代器的实际实现是否有特定要求。
因此,为了论证,您可以想象编译器除了使用实际地址之外还使用 "past end" 位来在内部实现普通的旧数组迭代器 和 如果它检测到迭代器和通过指针算术获得的地址之间存在任何可想象的不一致,就把你的胡子涂成粉红色。
这个怪异的编译器会导致许多现有的 C++ 代码崩溃而不会真正违反规范,这可能值得设计它的努力...
无效。您在问题中加粗了 "result is an lvalue referring to the object or function to which the expression points"。这正是问题所在。 array + size
是不指向对象的有效指针值。因此,您关于 *(array + size)
的引用没有指定结果所指的内容,这意味着 &*(array + size)
不需要给出与 array + size
.
相同的值
在 C 中,这被认为是一个缺陷并已修复,因此规范现在在 &*ptr
中说,&
和 *
都不会被评估。 C++ 还没有收到固定的措辞。这是一个非常古老但仍然活跃的 DR 的主题:DR #232。意图是它是有效的,就像在 C 中一样,但标准并没有这么说。
如果我们承认 arr[i]
只是 *(arr + i)
的 shorthand,我们可以将 &arr[size]
重写为 &*(arr + size)
。因此,我们取消引用指向尾后元素的指针,这会导致未定义的行为。正如您所说的那样, arr + size
反而是合法的,因为没有发生取消引用操作。
巧合的是,这在 Stepanov 的 notes(第 11 页)中也作为测验呈现。
假设我有一个这样调用的函数:
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,一元规则*
:
The unary
*
operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer toT
,” the type of the result is “T
.” [ Note: a pointer to an incomplete type (other than cvvoid
) can be dereferenced. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a prvalue, see 4.1. —end note ]
最后,5.3.1/3给出了&
的规则:
The result of the unary
&
operator is a pointer to its operand. The operand shall be an lvalue ... if the type of the expression isT
, the result has type “pointer toT
” and is a prvalue that is the address of the designated object (1.7) or a pointer to the designated function.
(强调和省略我的)。
我不能完全下定决心。我确信在 arr[size]
上强制进行左值到右值的转换是未定义的。但是代码中没有发生这种转换。 arr + size
没有指向对象;但是虽然上面的段落讨论了对象,但它们似乎从未明确指出对象实际存在于该位置的必要性(不像 4.1/1 中的左值到右值转换)。
所以,问题是:mysort
,它的调用方式是否有效?
(请注意,我在上面引用了 C++11,但如果在后面 standard/draft 中更明确地处理这个问题,我会非常高兴)。
只要大小不大于实际数组的大小(以数组元素为单位),它就非常好并且定义明确。
所以如果 main() 调用了 mysort(a, 100),&arr [size] 就已经是未定义的行为了(但很可能未被发现,但 std::sort 显然也会出错)。
在普通 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()
的明显选择。
但是,我怀疑对于普通旧数组迭代器的实际实现是否有特定要求。
因此,为了论证,您可以想象编译器除了使用实际地址之外还使用 "past end" 位来在内部实现普通的旧数组迭代器 和 如果它检测到迭代器和通过指针算术获得的地址之间存在任何可想象的不一致,就把你的胡子涂成粉红色。 这个怪异的编译器会导致许多现有的 C++ 代码崩溃而不会真正违反规范,这可能值得设计它的努力...
无效。您在问题中加粗了 "result is an lvalue referring to the object or function to which the expression points"。这正是问题所在。 array + size
是不指向对象的有效指针值。因此,您关于 *(array + size)
的引用没有指定结果所指的内容,这意味着 &*(array + size)
不需要给出与 array + size
.
在 C 中,这被认为是一个缺陷并已修复,因此规范现在在 &*ptr
中说,&
和 *
都不会被评估。 C++ 还没有收到固定的措辞。这是一个非常古老但仍然活跃的 DR 的主题:DR #232。意图是它是有效的,就像在 C 中一样,但标准并没有这么说。
如果我们承认 arr[i]
只是 *(arr + i)
的 shorthand,我们可以将 &arr[size]
重写为 &*(arr + size)
。因此,我们取消引用指向尾后元素的指针,这会导致未定义的行为。正如您所说的那样, arr + size
反而是合法的,因为没有发生取消引用操作。
巧合的是,这在 Stepanov 的 notes(第 11 页)中也作为测验呈现。