使用reinterpret_cast对类似“ struct {double,int}”的对象进行对象访问

Evg*_*Evg 2 c++ placement-new language-lawyer c++17

reinterpret_cast此处已广泛讨论了通过ed指针和相关的UB 访问对象。阅读问题和答案后,我仍然不确定是否可以将未初始化的内存与POD类型配合使用。

假设我想“模仿”

struct { double d; int i; };
Run Code Online (Sandbox Code Playgroud)

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

现在,我这样做:

struct { double d; int i; };
Run Code Online (Sandbox Code Playgroud)

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

我可以使用展示位置new

// (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;
Run Code Online (Sandbox Code Playgroud)

我一定要吗?new此处的放置似乎是多余的,因为POD类型的默认初始化为no-op(初始化),并且[basic.life]读取为:

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

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

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

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

第二个问题:我可以将type double*(或some T*)用于buff,即

// (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;
Run Code Online (Sandbox Code Playgroud)

要么

// (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;
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 5

正如Barry 所说的更好,1&3是UB。简短的版本:这些代码段都不包含创建对象所需的任何语法。而且,您无法访问不存在的对象的值。

那么,2和4是否起作用?

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

严格来说,#4无效。buff实际上并不指向新创建的double。因此,指针算术不能用于获取该对象之后的字节。因此,执行指针算术会产生未定义的行为。

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