这里已经广泛讨论了通过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;
?
答案 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
初始化,因此我认为代码不正确并且会调用未定义的行为。