【发布时间】: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* 之间的通信是合规的,因为该标准允许使用 char 和 unsigned char 指针(强调我的)进行别名:
如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义:
- 对象的动态类型,
- 对象动态类型的 cv 限定版本,
- 对应于对象动态类型的有符号或无符号类型,
- 一种有符号或无符号类型,对应于对象动态类型的 cv 限定版本,
- 在其成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含联合的成员),
- 一种类型,它是对象动态类型的(可能是 cv 限定的)基类类型,
char或unsigned char类型。
但是,我不确定f(*a) 是否通过创建对无效指针的A& 引用来调用未定义的行为。决定因素似乎是在 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