【问题标题】:Is &a[n] valid, where n is the size of the array? [duplicate]&a[n] 是否有效,其中 n 是数组的大小? [复制]
【发布时间】:2014-10-10 00:08:32
【问题描述】:

考虑以下简单代码对数组进行排序。

int myarray[4] = {};
std::sort(myarray, myarray + 4);

我知道创建一个指向 C 样式数组末尾之后的指针是有效的。

我最近看到过这样的代码:

std::sort(myarray, &myarray[4]);

我不确定这是否有效,因为它取消引用数组边界之外的元素,即使元素值没有用于任何事情。

这是有效的代码吗?

【问题讨论】:

  • Discussed in depth here 。 C++ 的结论似乎是 C++03 标准不清楚它是否有效。 (这个线程出现在 C++11 完成之前)。
  • @MattMcNabb:C++14 包含它吗?
  • 这个问题已经存在多年了,是时候让 CWG 中的某个人一劳永逸地解决它了 >.>
  • @BenVoigt 请注意(a)该问题是在 C++11 之前解决 DR232 的问题,并且(b)接受的答案是错误的。
  • @BenVoigt 他从一些不相关的标准引用开始,然后做出完全没有根据的结束声明“在我看来,这似乎暗示是的,你可以合法地取消引用它,但是阅读或写入的结果位置未指定。”。

标签: c++ language-lawyer


【解决方案1】:

A[i] 在语法上等同于 *(A + i) 用于数组或指针 A
所以&A[i] 在语法上等价于&(*(A + i))

*(A + i) 没有未定义的行为时,&(*(A + i)) 的行为将与A + i 相同。

问题在于myarray[4] 在语法上等价于*(myarray + 4),它取消引用数组边界之外的位置。根据标准,这是未定义的行为。

所以你绝对应该更喜欢myarray + 4 而不是&myarray[4] - 后者是未定义的行为。

&myarray[4] 具有“正确”行为,大多数(如果不是全部)编译器不会根据标准使其免于 未定义 行为。

【讨论】:

  • 是吗?即使 i 是元素数?
  • @Deduplicator 是的,它是一样的。
  • @TimothyShields 说谁?不是 C++ 标准。
  • @TimothyShields 在这种情况下 p == q 但这与 OP 的情况不同,因为 *p 在您的示例中有效。
  • 问题是它是否调用了UB。 “我感觉很幸运”和“它似乎有效”与此无关。如果你说它是有效的,那么标准的引用在哪里?
【解决方案2】:

它有效的原因与您的原件有效的原因相同:它可以确定该元素在哪里。在后一种情况下,仅仅因为它标识了一个不存在的元素,因为它没有尝试访问它,所以没有问题。

或者,更简洁地说:myarray+4&myarray[4] 是同义词。

【讨论】:

  • 是吗?确定取消引用不存在的元素后跟地址是有效的吗?证据在哪里?
  • ISO 2012 C++ 标准的第 5.2.1 节:“表达式 E1[E2] 与 *((E1)+(E2)) 相同(根据定义)”重复数据删除器有点模糊 - - 我相信他指的是下标等同于取消引用这一事实,因此前缀“&”没有帮助,因为您已经在 UB 土地上。当然,这不仅仅是有点挑剔 - 我真的怀疑那里有任何编译器不会做你期望的事情。
  • @joeking:问题仍然是“那是有效的代码吗”,唯一有效的标准是标准,而不是“它似乎碰巧有效,我期望如此”。
  • @joeking 如果我使用在调试模式下向数组添加边界检查的编译器会发生什么?
  • @joeking 这样的编译器只会在直接使用数组时执行它。从技术上讲,由于强制转换,代码是 UB,所以编译器的手被束缚了。
【解决方案3】:

该标准要求指向数组末尾的指针是该指针所具有的有效值。 (这并不意味着可以取消引用)。

这在一些地方是必需的,例如在指针比较(5.9,关系运算符)中:

如果两个指针指向同一个数组的元素或一个超出 数组末尾,指向高下标对象的指针 比较高。

这实际上依赖于 STL。迭代器的“end()”函数相当于数组末尾的1。

我还在第 27.6.2 节:流缓冲区要求中看到了这一点

所以你的 sn-p:

std::sort(myarray, &myarray[4]);

与使用int的向量基本相同

  vector<int> yourArray;
  std::sort(yourArray.begin(), yourArray.end());

【讨论】:

  • 结束后指针的有效性是毋庸置疑的。问题是在给定int arr8[count]; 的情况下&amp;arr[count] 是否有效。
  • 这有什么不同?如果数组大小为 4,则 &array[3] 是指向最后一个的指针,而 &array[4] 是指向过去 1 的指针。
  • 中间取消引用??
  • 什么?请把它拼出来。 "&myarray[4]" 不会取消引用任何内容 - 它需要地址,其中 "* & myarray[4]" 确实是无效的,因为您将取消引用指向末尾的指针。
  • &amp;arr[count] 取消引用末尾的一个(即 UB),然后获取地址。 arr+count 没有。问题是,由于标准的一些扭曲或明确的规则,尽管取消引用,第一个是否有效。在那里,常识和“它似乎有效”都不是可接受的答案。
【解决方案4】:

根据 C99 标准中的 §6.5.3.2,其有效 C:

一元 & 运算符产生其操作数的地址。如果操作数 类型为“type”,结果类型为“pointer to type”。如果 操作数是一元 * 运算符的结果,既不是该运算符也不是 & 运算符被求值,结果是好像两者都被省略了

在 C++ 中似乎没有等效的对应物,可能是由于运算符重载。

【讨论】:

  • C++ 中没有 6.5.3.2。您似乎引用了 C 标准。
  • @MattMcNabb:C++ 中没有这样的规则(从 2011 ISO C++ 标准开始,除非我遗漏了什么)。
  • which is probably an oversight 更可能没有包含它,因为 C++ 的运算符重载使得很难定义一个包罗万象的规则。
  • 这是 C 1999 标准的 6.5.3.2。所以在 C99 中,"&*X" 。它还说“操作数是 [] 运算符的结果,既不计算 & 运算符,也不计算 [] 所隐含的一元 *,结果就像删除了 & 运算符和 [] 运算符改为 + 运算符。”
  • @user657267:超载会造成什么困难?它可以只声明该规则仅适用于内置的一元 &amp; 运算符,而不适用于任何具有相同名称的重载运算符。
猜你喜欢
  • 2023-04-01
  • 2016-03-27
  • 2018-07-15
  • 2018-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-10
相关资源
最近更新 更多