通过重新解释转换

时间:2016-04-06 17:09:07

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

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

#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_cast来往char*是合规的,因为标准允许使用charunsigned char指针进行别名(强调我的):

  

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

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

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

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

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

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

[编辑] @SergeyA引用了标准的这一部分。我把它包括在这里以便于参考(强调我的):

  

5.3.1 / 1 [expr.unary.op]

     

一元*运算符执行间接:应用它的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是引用对象或函数的左值其中的表达点。如果表达式的类型是“指向T的指针”,则结果的类型为“T。”[注意:通过指向不完整类型(cv void除外)的指针间接有效。 如此获得的左值可以以有限的方式使用(例如,初始化参考);此左值不得转换为prvalue,请参阅4.1。 - 结束注释]

跟踪对4.1的引用,我们发现:

  

4.1 / 1 [conv.lval]

     

非函数非数组类型T的glvalue(3.10)可以转换为prvalue。如果T是不完整类型,则需要进行此转换的程序格式不正确。如果T是非类类型,则prvalue的类型是T的cv非限定版本。否则,prvalue的类型为T

     

将左值到右值转换应用于表达式e ,并且:或者

     
      
  • e无法评估,或
  •   
  • 评估e会导致评估ex的潜在结果的成员e,并ex命名变量x这不是由ex(3.2)
  • 使用的   
     

未访问引用对象中包含的值

我认为我们的答案在于*a是否满足第二个要点。我在解析这个条件时遇到了麻烦,所以我不确定。

1 个答案:

答案 0 :(得分:4)

char* x = reinterpret_cast<char*>(&f);有效。或者,更具体地说,允许通过x进行访问 - 演员本身始终有效。

A* a = reinterpret_cast<A*>(new char[5])无效 - 或者,准确地说,通过a进行访问会触发未定义的行为。

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

或者,在外行术语中,您可以为type*char*添加别名,但您不能char*通过type*

修改

我刚注意到我没有回答直接问题(&#34; 什么构成&#34;访问&#34;在C ++标准中&#34;)。显然,标准没有定义访问(至少,我无法找到正式的定义),但解除引用指针通常被理解为有资格访问。