使用值初始化的数组对零初始化数据

Zero-initialize data using value-initialized array

本文关键字:初始化 数据 数组      更新时间:2023-10-16

下面的代码会正确地初始化从malloc返回的内存吗?

#include <cstdlib>
#include <new>
int main()
{
char* p = new (std::malloc(10)) char[10]{};
}

是的,由于行末尾的{},只要malloc不会从放置new中失败,它将对内存进行零初始化。这里有一个替代版本,更完全地证明了这一点。

#include <iostream>
#include <cassert>
#include <new>
char mongo[10];
int main()
{
for (int i=0; i < 10; ++i)
mongo[i] = 'a';
char *p = new (mongo) char[10]{};
for (int i= 0; i < 10; ++i)
assert(p[i] == '');
}

由于p的初始化明确清除了mongo数组(由p别名),因此该程序将运行并不打印任何内容。相反,如果我们省略该行末尾的{},程序将断言并终止。

请注意,在使用malloc的版本中,您将必须显式地销毁对象——这是放置new的部分职责,也是现代C++中为数不多的必须显式调用析构函数的情况之一。

如果您使用malloc()分配内存,则必须发出相应的free(p),否则程序将泄漏内存。

房间里的大象

(感谢@TC指出灰色的大东西!)以下是直接来自标准的解释和示例(第5.3.4节):

示例:

new T导致呼叫operator new(sizeof(T))

new(2,f) T导致呼叫operator new(sizeof(T),2,f)

new T[5]导致呼叫operator new[](sizeof(T)*5+x),并且

CCD_ 19导致CCD_。

这里,x和y是表示数组分配开销的非负未指定值;新表达式的结果将从CCD_ 21返回的值偏移该量。此开销可应用于所有数组新表达式中,包括那些引用库函数运算符new[](std::size_t, void*)和其他放置分配函数的表达式。每次调用new时,开销的大小可能会有所不同。

换句话说,如果您对单个对象使用放置new,一切都很好,没有插入的开销,但如果您通过放置new分配数组,则可能会有开销,而且从一次调用到另一次调用,其大小甚至可能不相同。

这是一个非常非常糟糕的主意。

目前,布局数组new和所有数组new表达式一样,被允许添加未指定数量的开销。程序没有可移植的方法来知道这个开销是多少字节

因此,除非char[10]的实现的数组分配开销为零,否则您的代码具有未定义的行为。

如果指针指向足够的内存来容纳所有数组元素加上未指定的开销量(这是一个很大的If,因为没有可移植的方法可以确定),那么表达式确实会对内存进行零初始化。