【问题标题】:Object access using reinterpret_cast for "struct {double, int}"-like object使用 reinterpret_cast 访问类似“struct {double, int}”的对象的对象
【发布时间】:2019-10-20 08:54:32
【问题描述】:

这里已经广泛讨论了通过reinterpret_casted 指针和相关的UB 访问对象。阅读问题和答案后,我仍然不确定是否正确使用 POD 类型的未初始化内存。

假设我想“模仿”

struct { double d; int i; };

通过手动为数据成员分配内存并假设(为简单起见)i 之前不需要填充。

现在,我这样做:

// (V1)
auto buff = reinterpret_cast<char*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = reinterpret_cast<double*>(buff);
auto i_ptr = reinterpret_cast<int*>(buff + sizeof(double));
*d_ptr = 20.19;
*i_ptr = 2019;

第一个问题:这段代码有效吗?

我可以使用展示位置new

// (V2)
auto buff = reinterpret_cast<char*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = new(buff) double;
auto i_ptr = new(buff + sizeof(double)) int;
*d_ptr = 20.19;
*i_ptr = 2019;

我必须这样做吗?放置 new 在这里似乎是多余的,因为 POD 类型的默认初始化是无操作(空初始化),并且 [basic.life] 读取:

T 类型对象的生命周期开始于:

(1.1) 获得了T 类型的正确对齐和大小的存储,

(1.2)如果对象有非空初始化,则其初始化完成,...

这是否表示 *d_ptr*i_ptr 对象的生命周期在我为它们分配内存后开始?

第二个问题:我可以使用类型double*(或一些T*)作为buff,即

// (V3)
auto buff = reinterpret_cast<double*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = reinterpret_cast<double*>(buff);
auto i_ptr = reinterpret_cast<int*>(buff + 1);
*d_ptr = 20.19;
*i_ptr = 2019;

// (V4)
auto buff = reinterpret_cast<double*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = new(buff) double;
auto i_ptr = new(buff + 1) int;
*d_ptr = 20.19;
*i_ptr = 2019;

?

【问题讨论】:

  • @JesperJuhl,但我仍然想知道如果我真的必须要怎么做。
  • @supercat 什么......这甚至意味着什么以及它打算完成什么?
  • @supercat 我需要看到一些严肃的理由来证明编译器编写者不关心他们的客户会发现什么有用的想法。
  • @supercat 不知道这个例子是什么意思。
  • @Barry 在 supercat 的世界中,“质量编译器”是一种实现了他想到但从未能够成功表达的替代语言规范的编译器

标签: c++ language-lawyer c++17 placement-new


【解决方案1】:

作为 Barry states better here,1&3 是 UB。简短版本:这些代码中没有一个包含任何the syntax needed to create an object。而且您无法访问不存在的对象的值。

那么,2 和 4 有效吗?

#2 当且仅当alignof(double) &gt;= alignof(int) 有效。但它仅在创建double 后跟int 的意义上有效。它以任何方式“模仿”那个无名的结构。结构可以有任意数量的填充,而在这种情况下,int 将紧跟double

严格来说,#4 不起作用。 buff 实际上并不指向新创建的double。因此,指针算术cannot be used to get the byte after that object。所以做指针运算会产生未定义的行为。

现在,我们正在谈论 C++严格来说。很可能,每个编译器都会执行所有这四个(上面关于对齐的警告)。

【讨论】:

  • "创建对象所需的任何语法" 但是cout&lt;&lt;"hello world"; 也没有,但您认为一个是可以的。因此,您对 intro.object/1 是否认真和字面意思不太一致(无论是字面意思,认真还是两者都是 IMNSHO 的疯狂立场)。
【解决方案2】:

当我查看公开可用的草稿时,http://eel.is/c++draft/basic.life 的报价不同,它说

类型 T 的对象的生命周期开始于:

(1.1) 获得类型 T 具有适当对齐和大小的存储,并且

(1.2)它的初始化(如果有的话)已经完成(包括空初始化)([dcl.init]),

由于没有 vacuous 初始化 double 变量,我认为代码不正确并调用未定义的行为。

【讨论】:

  • 那句话(或任何地方)中的什么表明任何 OP 的代码格式不正确?
  • (部分)代码是未定义的行为,不是格式错误
  • @M.M 是不是表现出未定义行为的代码会使程序格式错误?
  • @SergeyA 不。格式错误的程序必须给出诊断,并且允许编译器不编译它们。
  • @SergeyA 一般来说,格式错误的代码涉及模板定义(不是实例化)的一些违反语言规则以及与链接有关的内容,但大多数其他格式错误的代码都需要诊断。格式良好的代码可以执行多次(取决于环境)。 一些执行导致 UB 不会使程序格式错误。
猜你喜欢
  • 1970-01-01
  • 2015-01-05
  • 2014-12-02
  • 2021-11-23
  • 1970-01-01
  • 1970-01-01
  • 2015-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多