使用 reinterpret_cast 访问类似 "struct {double, int}" 的对象

Object access using reinterpret_cast for "struct {double, int}"-like object

通过 reinterpret_casted 指针和相关 UB 访问对象已在此处进行了广泛讨论。阅读问题和答案后,我仍然不确定如何正确使用 POD 类型的未初始化内存。

假设我想"emulate"

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] 读取:

The lifetime of an object of type T begins when:

(1.1) storage with the proper alignment and size for type T is obtained,

(1.2) if the object has non-vacuous initialization, its initialization is complete, ...

这是否表示 *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;

?

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

The lifetime of an object of type T begins when:

(1.1) storage with the proper alignment and size for type T is obtained, and

(1.2) its initialization (if any) is complete (including vacuous initialization) ([dcl.init]),

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

作为巴里 , 1&3 are UB. The short version: none of those pieces of code contain any of the syntax needed to create an object。而且您无法访问不存在的对象的值。

那么,2 和 4 行得通吗?

#2 当且仅当 alignof(double) >= alignof(int) 时有效。但它仅在创建 double 后跟 int 的意义上有效。它不会以任何方式"emulate"那个无​​名结构。该结构可以有任意数量的填充,而在这种情况下,int 将紧跟在 double.

之后

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

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