【问题标题】:Can you access the object representation of any object through a char*?您可以通过 char* 访问任何对象的对象表示吗?
【发布时间】:2020-12-22 17:22:16
【问题描述】:

我偶然发现了reddit thread,其中用户发现了 C++ 标准的一个有趣细节。该线程没有产生太多建设性的讨论,因此我将在这里重述我对问题的理解:

  • OP 希望以符合标准的方式重新实现 memcpy
  • 他们试图通过使用 reinterpret_cast<char*>(&foo) 来做到这一点,这是严格别名限制的允许例外,其中允许重新解释为 char 以访问对象的“对象表示”。
  • [expr.reinterpret.cast] 表示这样做会导致 static_­cast<cv T*>(static_­cast<cv void*>(v)),因此在这种情况下,reinterpret_cast 相当于 static_cast'ing 先到 void * 然后再到 char *
  • [expr.static.cast] 结合[basic.compound]

“指向 cv1 void 的指针”类型的纯右值可以转换为“指向 cv2 T”的指针类型的纯右值,其中 T 是对象类型,而 cv2 与,或比 cv1 更高的 cv 限定。 [...] 如果原始指针值指向对象 a,并且有一个类型为 T(忽略 cv 限定)的对象 b 可以与 a 指针互转换,则结果是指向 b 的指针。 [...] [强调我的]

现在考虑以下联合类:

union Foo{
    char c;
    int i;
};
// the OP has used union, but iiuc,
// it can also be a struct for the problem to arise.

OP 因此得出结论,在这种情况下将Foo* 重新解释为char* 会产生一个指向联合体的第一个字符成员(或其对象表示)的指针,而不是指向联合体的对象表示的指针。 union 本身,即它只指向成员。虽然这表面上看起来是相同的,并且对应于相同的内存地址,但标准似乎区分了指针的“值”及其对应地址,因为在抽象 C++ 机器上,指针属于某个对象只要。将其递增到该对象之外(与数组的 end() 比较)是未定义的行为。

OP 因此认为,如果标准强制 char* 与对象的第一个成员而不是整个联合对象的对象表示相关联,则在一次增量后取消引用它是 UB,这允许编译器进行优化,就好像结果 char* 不可能访问 int 成员的以下字节。这意味着不可能合法地访问可与char 成员指针互转换的类对象的完整对象表示。

如果我理解正确的话,如果“union”被简单地替换为“struct”,同样会适用,但我从原始线程中获取了这个例子。

你怎么看?这是标准缺陷吗?是不是有误会?

【问题讨论】:

标签: c++ language-lawyer strict-aliasing


【解决方案1】:

@KonradRudolph 在 cmets(现在聊天)中链接的This video 可能是问题的答案。

在大约 40 分钟时,ISO C++ 委员会成员 Timur Doumler 讨论了访问字节表示的可能性。总结是除了memcpy 之外的任何访问字节表示的尝试都是UB。如果不使用 UB,OP 中的情况甚至不会出现,因为使用指向对象(如数组)的指针或对其进行任何指针运算的行为本身就是 UB,因为这些操作仅在处理时明确定义就抽象机器而言,实际的数组对象。

此外,虽然将指针重新解释为 char* 本身并不违反别名规则,但从技术上讲,不能保证生成的 char* 将指向对象的第一个字节。

访问字节表示的唯一合法方法是将memcpy 对象转换为char 数组。这意味着重新实现memcpy 是不可能的。

Timur Doumler 还将此描述为一个措辞缺陷,有望在 C++23 中得到修复,并发表了一篇论文,提出了对此的修复建议。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2016-04-29
  • 1970-01-01
  • 1970-01-01
  • 2020-12-03
  • 1970-01-01
  • 2017-05-28
  • 2014-03-10
  • 1970-01-01
相关资源
最近更新 更多