【发布时间】:2020-05-25 13:43:55
【问题描述】:
这是有效的 C++ 吗?
int main() {
int *p;
p = reinterpret_cast<int*>(42);
}
假设我从不取消引用 p。
查找 C++ 标准,我们有
C++17 §6.9.2/3 [basic.compound]
3每个指针类型的值都是以下之一:
- 指向对象或函数的指针(该指针被称为指向对象或函数),或
- 超过对象末尾的指针 ([expr.add]),或
- 该类型的空指针值 ([conv.ptr]),或
- 一个无效的指针值。
一个指针类型的值,它是一个指向或超过一个指针结尾的指针 object 表示内存中第一个字节的地址 ([intro.memory]) 对象或内存中的第一个字节占用 结束后对象分别占用的存储空间。 [ 注意:超过对象末尾的指针 ([expr.add]) 不是 被认为指向对象类型的不相关对象 可能位于该地址。指针值在以下情况下变为无效 它表示的存储达到其存储期限的结束;看 [基本.stc]。 — 尾注] 用于指针算术 ([expr.add]) 和比较 ([expr.rel], [expr.eq]),指针过去 考虑 n 个元素的数组 x 的最后一个元素的结尾 等效于指向 x 的假设数组元素 n 的指针 并且不是数组元素的类型 T 的对象被认为是 属于具有一个 T 类型元素的数组。
p = reinterpret_cast<int*>(42); 不适合可能的值列表。并且:
C++17 §8.2.10/5 [expr.reinterpret.cast]
整数类型或枚举类型的值可以显式 转换为指针。转换为整数的指针 足够的大小(如果实现中存在这样的大小)并返回到 相同的指针类型将具有其原始值;之间的映射 指针和整数是由实现定义的。 [ 笔记: 除 6.7.4.3 中所述外,此类转换的结果将 不是安全派生的指针值。 ——尾注]
C++ 标准似乎没有更多地说明整数到指针的转换。查看 C17 标准:
C17 §6.3.2.3/5(强调我的)
整数可以转换为任何指针类型。除了作为 先前指定,结果是实现定义的,可能不是 正确对齐,可能不指向被引用的实体 类型,并且可能是陷阱表示.68)
和
C17 §6.2.6.1/5
某些对象表示不需要表示 对象类型。如果一个对象的存储值有这样一个 表示并由不具有的左值表达式读取 字符类型,行为未定义。如果这样的表示是 由修改对象的全部或任何部分的副作用产生 通过没有字符类型的左值表达式, 行为未定义。50) 这种表示称为陷阱 表示。
对我来说,似乎任何不适合 [basic.compound] 列表的值都是陷阱表示,因此 p = reinterpret_cast<int*>(42); 是 UB。我对么?还有什么东西使p = reinterpret_cast<int*>(42); 未定义吗?
【问题讨论】:
-
第一个引用是关于指针value,而不是你如何获得它,所以我认为它在这里无关紧要。
reinterpret_cast<int*>(42)(可能)是一个无效的指针值,它适合您第一个引用的第 4 个项目符号。另外,“整数类型或枚举类型的值可以显式转换为指针。” - 这怎么不能回答你的问题? -
我读过 C++17 §6.9.2/3 ..“无效的指针值”作为指针值可能采用的四种可能和允许的形式之一。因此,“无效的指针值”(例如指向无效对象)仍然是定义的行为。 UB 来自定义指针操作含义的那些部分(例如算术、解引用)。在“无效指针值”上还有一个定义的行为,即“任何指针值都可以转换为整数类型”,无论指针值是否为“无效指针值”。
-
@Ayxan 如果指针只有在它们指向的对象达到其存储期限结束时才会变为无效,
int *p将是 UB,所以我不认为您采用该引用的方式是对。 -
顺便说一句,
42可能是int*的未对齐值。 -
@Holt:是UB:eel.is/c++draft/basic.indet#2
标签: c++ pointers casting language-lawyer reinterpret-cast