运算符new将内存初始化为零


78

有这样的代码:

#include <iostream>

int main(){
  unsigned int* wsk2 = new unsigned int(5);
  std::cout << "wsk2: " << wsk2 << " " << *wsk2 << std::endl;
  delete wsk2;
  wsk2 = new unsigned int;
  std::cout << "wsk2: " << wsk2 << " " << *wsk2 << std::endl;
  return 0;
}

结果:

wsk2: 0x928e008 5
wsk2: 0x928e008 0

我读过new没有用零初始化内存。但是在这里看来确实如此。它是如何工作的?


2
不需要,您必须使用新的unsigned int()。读取未初始化的变量是UB,肯定可以为0。
汉斯·帕桑

1
顺便说一句,实际上,很多内存还是为零。如果您对大量内存进行简单的malloc分配,则在某些系统上通常全为零,而在其他系统上通常为全零。
尼古拉斯·威尔逊

2
@NicholasWilson:对于大多数系统,仅当内存直接来自操作系统时才如此(出于安全原因)。当它来自分配器缓存的内存时,它很可能包含它在free()d之前所做的任何事情。这也是为什么有时在测试用例/单元测试中不会出现此类错误,而是仅在“真实”程序运行一段时间后才会出现此类错误的原因。瓦尔格朗德在这里营救。
PlasmaHH 2011年

是。这是我的观点。如果您创建了一个新变量,并且看到它为零,那么您将无法立即假设程序中的某些内容将其设置为零。由于大多数内存已准备就绪,因此可能仍未初始化。我认为我的话语实际上比下面的大多数答案更接近于回答问题,因为发问者想知道为什么未初始化的内存可能为零。
尼古拉斯·威尔逊

尝试分配一个数组,将所有项目设置为某个非零值,然后将其删除[]并再次分配-仅将第一个项目清零-其他保留其值-我认为这是内存分配器的特殊性(在Linux下进行了测试)。
kyku

Answers:


199

有两个版本:

wsk = new unsigned int;      // default initialized (ie nothing happens)
wsk = new unsigned int();    // zero    initialized (ie set to 0)

也适用于数组:

wsa = new unsigned int[5];   // default initialized (ie nothing happens)
wsa = new unsigned int[5](); // zero    initialized (ie all elements set to 0)

在回答下面的评论。

嗯...您确定new unsigned int[5]()将整数归零吗?

显然是的:

[C ++ 11:5.3.4 / 15]:创建类型T的对象的new表达式按如下方式初始化该对象:如果省略了new-initializer,则该对象将被默认初始化(8.5);否则,该对象将被初始化。如果未执行初始化,则该对象具有不确定的值。否则,将根据8.5的初始化规则对新初始化程序进行解释以进行直接初始化。

#include <new>
#include <iostream>


int main()
{
    unsigned int   wsa[5] = {1,2,3,4,5};

    // Use placement new (to use a know piece of memory).
    // In the way described above.
    // 
    unsigned int*    wsp = new (wsa) unsigned int[5]();

    std::cout << wsa[0] << "\n";   // If these are zero then it worked as described.
    std::cout << wsa[1] << "\n";   // If they contain the numbers 1 - 5 then it failed.
    std::cout << wsa[2] << "\n";
    std::cout << wsa[3] << "\n";
    std::cout << wsa[4] << "\n";
}

结果:

> g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.2.0
Thread model: posix
> g++ t.cpp
> ./a.out
0
0
0
0
0
>

那么重载new/new[]运算符呢?将new unsigned int[5]()仍然是零初始化,即使它有一个专有的,完全不相关的实施阵列?
SomeWittyUsername 2013年

2
@icepack:operator new是用户(开发人员)定义的函数,它可以执行任何喜欢的操作。该标准没有对该功能的作用施加限制。就像operator==可以做用户定义的任何事情一样(它甚至不需要返回布尔值,更不用说测试是否相等了(尽管如果不是,则是不好的形式))。
马丁·约克


1
谢谢。只是要清楚一点:我专有的分配器的实现与零初始化无关,如果使用()语法,缓冲区将被零初始化而与分配的对象的类型及其构造函数无关吗?(而且我想C ++ 11在这方面没有任何改变)
SomeWittyUsername

1
@ J3STER在新调用中传递指针称为“新放置”。请参阅8.3.4 New。为什么要修改。因为我们要强制值初始化。请参阅11.6 Initializers。请注意,我们正在传递一个指向new运算符的指针。的值wsa未更改,它是指向的地址已更改。
马丁·约克

21

operator new不能保证将内存初始化为任何东西,并且分配一个不带new-initializernew-expression使该对象具有不确定的值。unsigned int

读取未初始化对象的值会导致未定义行为未定义的行为包括将值评估为零而没有不良影响,但可能会导致任何事情发生,因此应避免造成此情况。

在C ++ 11中,使用的语言是所分配的对象是默认初始化的,对于非类类型,这意味着不执行任何初始化。这与C ++ 03中default-initialized的含义不同。


4

对于某些编译器,new的调试版本将初始化数据,但是肯定没有什么可以依赖。

内存的前一次使用也可能只有0。不要以为删除和新建之间的内存没有任何变化。在后台可能发生了您从未注意到的事情。同样,相同的指针值可能不是相同的物理内存。内存页面被移动并调出和调入。指针可能被映射到与之前完全不同的位置。

底线:如果您没有专门初始化一个内存位置,那么您可以不假设其内容。在使用内存之前,内存管理器甚至可能不会分配特定的物理内存位置。

现代内存管理非常复杂,但是作为C ++程序员,您并不在乎(大多数情况下)。遵守规则,您就不会遇到麻烦。

•您可能会担心是否要进行优化以减少页面错误。


3

那不是operator new,那是new运营商。实际上有很大的不同!不同之处在于它operator new是一个返回原始内存的函数。当您使用new运算符时,它会为您调用一个构造函数。设置那个值的是构造函数int,而不是operator new


5
我认为正确的术语是“新表达式”,它调用相应的新运算符。
Kerrek SB 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.