【问题标题】:memcpy() on struct member casted from an opque pointer从 opque 指针投射的结构成员上的 memcpy()
【发布时间】:2020-04-27 07:26:06
【问题描述】:

假设我有一个 API:

// api.h - Others can #include this header
#include <cstdint>

class A {
 public:
  // Write data into an opaque type.
  // The user shouldn't directly access the underlying bits of this value.
  // They should use this method instead.
  void WriteVal(uint32_t data);

 private:
  uint64_t opaque_value_;
};
// api.cpp
#include <cstdlib>
#include "api.h"

namespace {

// This is the underlying struct that the opaque value represents.
struct alignas(8) Impl {
  uint32_t x;
  uint32_t y;
};

}  // namespace

void A::WriteVal(uint32_t data) {
  uint64_t *opaque_ptr = &opaque_value_;
  Impl *ptr = reinterpret_cast<Impl *>(opaque_ptr);
  memcpy(&ptr->y, &data, sizeof(data));
}

A::WriteVal 方法中是否有任何未定义的行为?

出于以下原因,我的猜测是否定的:

  1. 重新解释 uint64_t *Impl * 之间的转换是可以的,因为指针类型的对齐方式是相同的。
  2. 如果要显式取消引用 ptr,则只有 UB,因为这会违反严格的别名规则。
  3. 无论指针的原始类型如何,memcpy 都可以安全地用于替代显式取消引用。

我的推理正确吗?如果这也被认为是 UB,那么是否有一种很好的 C++ 方式来写入不透明类型而没有非法类型双关语方法。

我的目标是干净地对不透明值执行操作,在底层,它代表用户不应该知道详细信息的结构。

【问题讨论】:

  • 为什么需要Impl?为什么不简单地将memcpy 改为reinterpret_cast&lt;char*&gt;(&amp;opaque_value_) + sizeof(uint32_t)?顺便说一句,您在 &amp;ptr 中明确取消引用 ptr,不是吗?
  • 您仍然有可能的结构填充问题。不过,正如 DanielLangr 所建议的那样,不需要该结构。
  • 您可能还需要警惕字节序。
  • @DanielLangr 这是一个简化的例子。 Impl 可以是具有任意数量的任意字段的结构来表示一些复杂的逻辑。这背后的想法是它更简洁,并更多地利用类型系统让编译器通过成员访问运算符获取成员的偏移量,而不是强制转换为char * 并采用offsetof。此外,&amp;ptr-&gt;y 实际上并没有取消引用指针。这相当于你提到的演员然后偏移,但更清洁。如果您也为此查看程序集,您会发现 ptr 实际上从未被取消引用。
  • @LeoCHan 对不起,我忽略了自己,你是对的,你没有取消引用ptr。但是,正如 Sander 指出的那样,填充有问题(虽然不太可能,但xy 之间可能存在一些问题)。另外,我不确定uint64_t的对齐是否保证为8。理论上,可能会更多。但是您可以静态断言Impluint64_t 的大小和对齐方式相同。

标签: c++ undefined-behavior type-punning opaque-pointers


【解决方案1】:

您的推理涵盖了由 strict aliasing 违规引起的未定义行为。使用memcpy 确实是一种以定义的方式进行类型双关的方法。

不过,还有其他潜在的未定义行为来源。在您的示例代码中,还应控制结构的对齐和填充:

struct alignas(uint64_t) Impl {
  uint32_t x;
  uint32_t y;
};
static_assert(sizeof(Impl) == sizeof(uint64_t), "sizeof(Impl) not valid");

我在您的示例代码中看不到任何其他可能的未定义行为来源。

【讨论】:

    猜你喜欢
    • 2014-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-07
    相关资源
    最近更新 更多