【问题标题】:Is dereferencing invalid pointers legal if no lvalue-to-rvalue conversion occurs如果没有发生左值到右值的转换,取消引用无效指针是否合法
【发布时间】:2018-04-23 23:44:51
【问题描述】:

尽我所能,我见过的最接近的答案是this,有两个完全相反的答案(!)

问题很简单,这样合法吗?

auto p = reinterpret_cast<int*>(0xbadface);
*p;  // legal?

我对此事的看法

  1. Casting integer to pointer: 对可以投射的内容没有限制
  2. Indirection:只声明结果是左值。
  3. Lifetimes: 只说明对象不能做的事情,这里没有对象
  4. Expression statements: *p 是一个废弃的值表达式
  5. Discarded value expressions:没有发生左值到右值的转换
  6. Undefined-ness of lvalues: 也就是严格的别名规则,仅当左值被转换为右值时

所以我得出的结论是,没有什么明确说这是未定义的行为。然而,我清楚地记得,一些平台会为无效指针设置间接陷阱。我的推理出了什么问题?

【问题讨论】:

  • 你真的认为这合法吗?
  • @Passer “它可能产生副作用的唯一方法是如果它是 UB ...” 你抓住了我的想法 :-)
  • 来自标准:“结果是一个左值,指的是表达式指向的对象或函数”。您的指针不指向任何对象,因此这一行不适用,并且该段落中没有任何其他内容定义结果。

标签: c++ language-lawyer


【解决方案1】:

[basic.compound] 说:

指针类型的每个值都是以下之一:

  • 指向对象或函数的指针(据说该指针指向对象或函数),或
  • 超过对象末尾的指针 ([expr.add]),或
  • 该类型的空指针值 ([conv.ptr]),或
  • 一个无效的指针值。

通过排除过程我们可以推断出p是一个无效的指针值。

[basic.stc] 说:

通过无效指针值间接并传递无效 指向释放函数的指针值具有未定义的行为。任何 无效指针值的其他用途具有实现定义 行为。

由于间接运算符被称为通过[expr.unary.op] 执行间接,我会说,表达式*p 会导致UB,无论结果是否使用。

【讨论】:

  • 嗯,这显然是答案。这也证明了链接的问题接受了错误的答案。
  • @PasserBy 值得注意的是,实现是允许创建对象的,如果它放宽了指针安全性,通过 not-safely-derived 指针访问有效对象可能是合法的。它允许像volatile int* output_pins = reinterpret_cast&lt;int*&gt;(0x0800); 这样的东西在特定实现(如嵌入式编程)中是合法的。我假设 0xbadface 不是指向有效对象的指针。
  • 如果 0xbadface 指向映射到内存空间的硬件整数怎么办?
  • 通过排除法我们可以推断出 p 是一个无效的指针值。 100% 不同意。你是如何推断出这一点的?从整数到指针的映射(反之亦然)主要是实现定义的,并且 nothing 禁止实现为您提供一个 pointer 到 一些现有对象作为reinterpret_cast&lt;int*&gt;(0xbadface) 的结果。
【解决方案2】:

...某些平台会在无效指针的间接寻址上陷入陷阱。

大多数平台在无效地址访问上陷入陷阱。这与问题没有任何矛盾。 *p; 中发生了什么的问题归结为是否发生了在无效地址上实际获取的尝试。

获取的问题与core issue 232(通过空指针间接)非常相似。正如您已经指出的那样,*p;discarded value expression,因此不会发生 lvalue-to-rvalue conversion(“获取”):

汤姆·普拉姆:

...只有“获取”、左值到右值转换的行为才会触发格式错误或未定义的行为。

随后:

2003 年 10 月会议记录:

我们同意标准中的方法似乎没问题:p = 0; *p; 是 本质上不是错误。左值到右值的转换会给它 未定义的行为。


至于reinterpret_cast&lt;int*&gt;(0xbadface) 是否产生一个有效指针,确实在严格指针安全的实现中,它不会是一个安全派生的指针,并且因此是无效的,任何使用它都是UB。

但在relaxed pointer safety 的情况下,生成的指针是有效的(否则就不可能使用从二进制库和用 C 或其他语言编写的组件返回的指针)。

【讨论】:

  • 如果p 肯定有一个无效的指针值怎么办? int *p = new int; delete p; *p;。是UB吗?
  • 是的,这就是UB,存储期限已到。该实现可以显式检查并捕获取消引用已删除指针。
  • 但是,这里没有左值到右值的转换。这是不同的情况吗?可以取消引用空指针(无需从左到右转换),但不能取消引用无效指针?
  • 随机地址假定不指向一个对象或一个过去另一个对象,它显然不是一个空指针。那么它只能是一个无效的指针。 [basic.stc] 没有给出无效指针的定义,只是获取指针的一种可能方式。
  • 谢谢,已更新答案。恕我直言,该标准在 [basic.stc] 中如何应用术语 indirection 方面存在缺陷。我认为他们的意思是说“间接后跟从左到右的转换”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多