【问题标题】:C++: Is memcpy to POD based sub-object UB?C++:memcpy 是基于 POD 的子对象 UB 吗?
【发布时间】:2020-08-12 17:42:52
【问题描述】:

让我们进入代码:

extern "C" {
#include "pod-struct-T.h"
#include "malloc-and-initialize-one-T.h"
}

struct TCpp : T
{
   TCpp()
   {
      T* ptr_t = malloc_and_initialize_one_T();
      T* this_t = static_cast<T*>(this);
      std::memcpy(this_t, ptr_t, sizeof(T));
      free(ptr_t);
   }
};

这段代码中有多少 UB(对于 C++03 和 C++11;或者对于 C++20,如果在重新访问内存模型后发生了变化)或者这样做 memcpy 只是美好的?如果是 UB,它是否至少在主要编译器中可移植? (gcc、clang、intel 等)。

免责声明:是的,我知道...,这很丑陋,但不要问我为什么需要这样做。

【问题讨论】:

  • 此代码中唯一的 UB 是 malloc_and_initialize_one_T() 返回一个您没有检查的 NULL 指针,或者 T 具有非 POD 成员或指针成员。除此之外,您根本不需要static_castT* this_t = this; 工作得很好,因为TCpp 派生自T
  • 我同意@RemyLebeau。只要ptr_t 没有任何问题,static_castmemcpyfree 都是安全的。也就是说,malloc_and_initialize_one_T 返回一个正确对齐的 + 大小、非空值、T*T 实际上是一个 POD。
  • @LeoCHan @LeoCHan T 可以是 POD,但在复制到其中时仍然存在问题,即,如果它包含作为指针的成员,它们本身就是 POD,或引用。
  • @RemyLebeau 实际上,在我的例子中,“POD”结构确实包含指针,但这种情况是完全可控的。
  • 内存复制到可能重叠的子对象(包括基类子对象)是绝对不允许的,因为潜在的填充重用。

标签: c++ c++11 inheritance malloc language-lawyer


【解决方案1】:

规则非常明确。 [basic.types]/3(强调添加,脚注省略):

对于任何可简单复制的类型T,如果两个指向T 的指针指向 不同的T 对象obj1obj2其中obj1obj2 都不是 潜在重叠的子对象,如果底层字节 ([intro.memory]) 将obj1 复制到obj2obj2 应 随后保持与obj1 相同的值。

Base-class subobjects are potentially-overlapping subobjects. 因此,如果您将memcpy 放入基类子对象中,标准并没有定义结果值是什么。

简单的赋值有什么问题?

【讨论】:

  • 是的,我不知道为什么当行为完全定义时我没有想到简单的赋值。我现在在我的实施中改变了这一点,谢谢。我想我的头脑处于“低级编程模式”,我错过了大局。
  • 我猜memcpy 对基类子对象的格式不正确,因为没有明确允许这样做的规则,也没有将其声明为 UB。
  • @Peregring-lk 术语“格式错误”适用于必须由编译器诊断的代码,或者可能导致编译器拒绝带有诊断的代码,但不必,就像缺少函数定义(编译器从不拒绝缺少非虚函数定义的程序,但它们可以)。根据定义,在运行时做非法的事情是无法诊断的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-16
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
相关资源
最近更新 更多