将创建数组对象的malloc操作返回的指针值转换为元素类型会导致UB


struct T{
T(){}
~T(){}
};
int main(){
auto ptr = (T*)malloc(sizeof(T)*10); // #1
ptr++;
}

由于T不是隐式生存期类,因此操作malloc永远不能隐式创建类类型为T的对象。在本例中,我们打算使ptr指向具有10个类型为T的元素的数组对象的初始元素。基于这个假设,ptr++将具有有效的行为。根据[interro.object]p11

此外,在指定的存储区域内隐式创建对象后,一些操作被描述为生成指向适当创建的对象的指针。这些操作选择一个隐式创建的对象,其地址是存储区域开始的地址,并且生成一个指向该对象的指针值(如果该值将导致程序具有定义的行为(。

由于任何类型的数组都是隐式生存期类型,malloc(sizeof(T)*10)可以隐式创建一个包含10个元素的数组对象,并开始该数组对象的生存期。由于初始元素和包含数组不是指针可交换的ptr永远不能指向该数组的初始元素。相反,该操作只能生成数组对象的指针值。有了这个假设,我们应该把#1分为几个步骤,使程序形成良好的形式?

// produce the pointer value points to array object of 10 T
auto arrPtr = (T(*)[10])malloc(sizeof(T)*10);  

// decay the array 
auto ptr =*arrPtr; // use array-to-pointer conversion 
ptr++; 

我们应该首先获取数组指针,并使用该指针值来获取初始元素指针值?这些过程对于使程序形成良好的形式有必要吗?

我认为第一个例子非常有效,但毫无意义。malloc返回的数组尚未初始化任何对象。虽然我认为你可以形成一个指向每个对象的指针并在数组上迭代,但你在任何时候都不能取消引用指针。

在我看来,这里唯一缺少的是使用placement new或construct_at,如下所示:

struct T{
T(){}
~T(){}
};
int main(){
constexpr int SIZE = 10;
auto ptr = (T*)malloc(sizeof(T)*SIZE); // #1
for (auto p = ptr; p < &ptr[SIZE]; ++p) {
std::construct_at(p);
}
ptr++;
}

数组中的对象只能使用指针算术或数组索引ptr[i]来构造,这相当于*(ptr+i(,因此与p++基本相同。如果您的初始示例是UB,那么上面的construct_at调用也将是UB。唯一的解决方法是将malloc的返回视为字节数组,并在每个大小为(T(字节的情况下调用construct_at。在构建之后,我希望阵列可以被铸造成正确的类型。但这是愚蠢的,我希望没有人能找到必要的理由。

总的来说,如果你的例子是非法的,那么你将如何实现new((本身?

相关内容

最新更新