【问题标题】:Creating an invalid reference via reinterpret cast通过重新解释强制转换创建无效引用
【发布时间】:2016-07-27 06:03:17
【问题描述】:

我正在尝试确定以下代码是否调用了未定义的行为:

#include <iostream>

class A;

void f(A& f)
{
  char* x = reinterpret_cast<char*>(&f);
  for (int i = 0; i < 5; ++i)
    std::cout << x[i];
}

int main(int argc, char** argue)
{
  A* a = reinterpret_cast<A*>(new char[5])
  f(*a);
}

我的理解是 reinterpret_casts 与 char* 之间的通信是合规的,因为该标准允许使用 charunsigned char 指针(强调我的)进行别名:

如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义:

  • 对象的动态类型,
  • 对象动态类型的 cv 限定版本,
  • 对应于对象动态类型的有符号或无符号类型,
  • 一种有符号或无符号类型,对应于对象动态类型的 cv 限定版本,
  • 在其成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含联合的成员),
  • 一种类型,它是对象动态类型的(可能是 cv 限定的)基类类型,
  • charunsigned char 类型。

但是,我不确定f(*a) 是否通过创建对无效指针的A&amp; 引用来调用未定义的行为。决定因素似乎是在 C++ 标准上下文中“尝试访问”措辞的含义。

我的直觉是,这构成访问,因为访问需要定义A(它已声明,但在此示例中未定义)。不幸的是,我在 C++ 标准中找不到“访问”的具体定义:

f(*a) 会调用未定义的行为吗?什么构成 C++ 标准中的“访问”?

我了解,无论答案如何,在生产代码中依赖此行为都可能是个坏主意。我问这个问题主要是为了提高我对语言的理解。

[编辑] @SergeyA 引用了标准的这一部分。为了便于参考,我将其包含在此处(强调我的):

5.3.1/1 [expr.unary.op]

一元* 操作符执行间接:应用它的表达式应该是一个指向对象类型的指针,或者是一个指向函数类型的指针,结果是一个左值,指的是对象或函数。表达点。如果表达式的类型是“指向T 的指针”,则结果的类型是“T”。 [注意:通过指向不完整类型(除了 cv void)的指针间接是有效的。 由此获得的左值可以以有限的方式使用(例如初始化引用);此左值不得转换为纯右值,请参见 4.1。 — 尾注]

追溯对4.1的引用,我们发现:

4.1/1 [conv.lval]

非函数、非数组类型T 的泛左值(3.10)可以转换为纯右值。如果T 是不完整类型,则需要进行此转换的程序格式错误。如果 T 是非类类型,则纯右值的类型是 T 的 cv 非限定版本。否则,prvalue的类型为T

当左值到右值的转换应用于表达式e,并且:

  • e 未被潜在评估,或
  • e 的评估导致e 的一组潜在结果中的成员ex 的评估,并且ex 命名了一个变量x,它不是由ex 使用的 odr (3.2)

引用的对象没有被访问中包含的值。

我认为我们的答案在于*a 是否满足第二个要点。我无法解析该条件,所以我不确定。

【问题讨论】:

  • A的对齐方式是什么?

标签: c++ standards language-lawyer reinterpret-cast strict-aliasing


【解决方案1】:

char* x = reinterpret_cast&lt;char*&gt;(&amp;f); 有效。或者,更具体地说,允许通过 x 进行访问 - 强制转换本身始终有效。

A* a = reinterpret_cast&lt;A*&gt;(new char[5]) 无效 - 或者,准确地说,通过 a 访问将触发未定义的行为。

原因是虽然可以通过char* 访问对象,但不能通过随机对象访问字符数组。标准允许第一个,但不允许第二个。

或者,通俗地说,您可以将type* 别名到char*,但不能将char* 别名到type*

编辑

我刚刚注意到我没有回答直接问题(“C++ 标准中的“访问”是什么”)。显然,标准没有定义 access (至少,我无法找到正式的定义),但取消引用指针通常被理解为有资格访问。

【讨论】:

  • 我同意“通过a 访问将触发未定义的行为”,但是(就像您提到的那样),A* a = reinterpret_cast&lt;A*&gt;(new char[5]) 可能不构成访问。再次,答案归结为什么构成了“访问”。是 pointer deference 还是 conversion to an lvalue(我可能会弄错类型)“访问”?
  • A* a = ... 不是访问权限。但是取消引用指针是一种访问。
  • 很抱歉学究气,但是为什么不?我不清楚为什么A&amp; b = *a 本质上比A* b = a 更具有访问权限。这些语句都不需要定义A;只宣布。标准中是否有任何暗示这种解释的内容?
  • 是的,我说了好几次 - 取消引用指针。一个特定的标准术语是indirection,并且在5.3.1 / 1 中指定,允许对不完整类型的间接初始化引用。但是,它仍然被理解为访问。
  • 感谢您提供指向 5.3.1 / 1 的指针 - 这绝对是相关的。但是,我特别在标准中寻找一个声明,说明取消引用指针(间接)构成“访问”。我遵循了对 4.1 / 1 的参考,该参考定义了左值到纯右值的转换何时构成访问。我不肯定,但我认为A&amp; b = *a 不是访问,因为结果不是 odr-used。
猜你喜欢
  • 2014-11-29
  • 2020-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多