【问题标题】:Can a reinterpret_cast change the object representation?reinterpret_cast 可以更改对象表示吗?
【发布时间】:2018-08-25 21:32:42
【问题描述】:

我对 reinterpret_cast 的心智模型一直是,将表达式的位序列视为不同类型,而 cppreference(注意:这不是 C++ 标准的引用)似乎同意这一点:

与static_cast 不同,但与const_cast 类似,reinterpret_cast 表达式不会编译为任何 CPU 指令。它纯粹是一个编译器指令,指示编译器将 expression 的位序列(对象表示)视为具有 new_type 类型。

在寻找保证时,我偶然发现了[expr.reinterpret.cast] 下的一个注释:

[ 注意:reinterpret_­cast 执行的映射可能会或可能不会产生与原始值不同的表示。 — 尾注 ]

这让我想知道:reinterpret_cast 在什么条件下会产生一个对象表示与原始值不同的值?

【问题讨论】:

  • 没有强制转换会改变它们所应用的对象,如果这是您所要求的。
  • @NeilButterworth:我不觉得,那个编辑添加了任何已经不存在的东西。
  • 立即想到一种情况,即不同的指针类型具有不同的对齐要求并主动将低位位清零。在这样的平台上,从不太严格的对齐要求转换为更严格的对齐要求将使一些位清零。
  • @AnT:这帮助我理解了,为什么 reinterpret_cast 往返 (T1* → T2* → T1*) 只保证返回相同的对象表示,以防@987654333 @ 的对齐要求不比 T1 严格。谢谢你。

标签: c++ language-lawyer


【解决方案1】:

这是一个示例:如果您阅读了4th 要点:

指针可以显式转换为任何大到足以容纳其类型的所有值的整数类型。映射函数是实现定义的。 [ 注意:对于那些知道底层机器的寻址结构的人来说,这并不奇怪。 — 尾注 ]

现在,它是实现定义的,i 的值将在这里:

void *ptr = <some valid pointer value>;
uintptr_t i = reinterpret_cast<uintptr_t>(ptr);

它可以是任何东西,只要reinterpret_casting i 回来我们会得到ptr。

ptr 和 i 的表示可能不同。标准只是说i 的值应该“不足为奇”。甚至,如果我们将reinterpret_castptr 转换为更宽的整数(例如,如果指针是 32 位,则转换为 unsigned long long int),表示必须不同,因为变量不同。

所以我认为cppreference描述有误导性,因为可以有reinterpret_casts,实际上需要CPU指令。


Here 是另一个案例(由 IInspectable 发现),Keith Thompson 的评论:

用于 Cray 向量机(例如 T90)的 C 编译器执行类似的操作。硬件地址为 8 个字节,指向 8 个字节的字。 void* 和 char* 在软件中处理,并在字内增加了 3 位偏移量——但由于实际上没有 64 位地址空间,因此偏移量存储在高位 3 位64 位字。所以 char* 和 int* 的大小相同,但具有不同的内部表示——假设指针“真的”只是整数的代码可能会严重失败。

char * 和 int * 在 Cray T90 上有不同的表示,所以:

int *i = <some int pointer value>;
char *c = reinterpret_cast<char *>(i);

这里,i 和 c 在 Cray T90 上会有不同的表示(并且进行这种转换肯定会使用 CPU 指令)。

(我已经验证了这一点,Cray C/C++ Reference Manual SR–2179 2.0 的第 3.1.2.7.1 章)

【讨论】:

  • 我仍然不明白 reinterpret_cast 应该如何神奇地进行这些编译时类型转换。这个问题可以很简单地通过发布发出的汇编代码来说明它何时发生。
  • @NeilButterworth:“神奇地”是什么意思?在我的示例中,我们有一个指针。它被转换为整数。映射是实现定义的,可以是任何东西。因此,表示也可以是任何东西,即它可以改变。故事结束。
  • @NeilButterworth: 给定#include &lt;stdint&gt; / uint64_t foo(void *x) { return reinterpret_cast&lt;uint64_t&gt;(x); },使用c++ -O3 -m32 -S 调用的Apple LLVM 9.1.0 (clang-902.0.39.2) 生成movl 8(%ebp), %eax / xorl %edx, %edx,因此将其作为输入一个四字节对象并产生一个八字节对象作为输出。
  • 注意:注释是非规范性的。编译器可以随意忽略它们,它们仍然符合 C++ 标准。
  • @Yakk-AdamNevraumont:我已经多次看到这个注释,这些信息写在哪里?我的意思是,哪一部分是规范的,哪一部分不在标准中(假设'['和']'之间的部分是非规范的)?我没有在 C++ 标准本身中找到它。它在一些参考文件中吗?
【解决方案2】:

你是正确的 reinterpret_cast 不会改变位值,但这并不意味着结果值不会改变。

一个简单的例子是将 32 位整数类型转换为 char[4],每个元素代表 IPv4 地址的一个八位字节。

【讨论】:

  • 这是一个简单的例子吗?解释更多。
  • 那个演员 - 据我所知 - 保留了对象表示。
  • 实际上,您误读了这个问题。它确实明确表示,强制转换可以更改对象表示(位值)。
猜你喜欢
  • 2013-10-18
  • 1970-01-01
  • 1970-01-01
  • 2022-11-25
  • 1970-01-01
  • 2016-02-08
  • 1970-01-01
  • 1970-01-01
  • 2016-10-22
相关资源
最近更新 更多