使用reinterpret_cast对类似于“结构{double,int}”的对象

时间:2019-06-04 19:50:46

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

这里已经广泛讨论了通过reinterpret_cast ed指针和相关的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类型的默认初始化是no-op(空初始化),并且 [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;

2 个答案:

答案 0 :(得分:5)

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

那么2和4起作用吗?

#2在且仅当alignof(double) >= alignof(int)时有效。但这仅在创建double后跟int的意义上起作用。它不会以任何方式“模仿”该无名的结构。该结构可以有任意数量的填充,而在这种情况下,int将立即跟随double

严格来讲,#4不起作用。 buff实际上并不指向新创建的double。这样,指针算术cannot be used to get the byte after that object。因此,执行指针算术会产生不确定的行为。

现在,我们正在严格地讲C ++。。每个编译器极有可能会执行所有这四个操作(以上关于对齐的警告)。

答案 1 :(得分:1)

当我查看公开提供的草稿时,http://eel.is/c++draft/basic.life的引用是不同的,它说的是

  

类型为T的对象的生存期始于以下时间:

     

(1.1)获得类型T正确的对齐方式和大小的存储,并且

     

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

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