【问题标题】:Operator new initializes memory to zero运算符 new 将内存初始化为零
【发布时间】:2011-11-24 16:29:30
【问题描述】:

有这样的代码:

#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 不会用零初始化内存。但在这里似乎确实如此。它是如何工作的?

【问题讨论】:

  • 它没有,你必须使用新的 unsigned int()。读取未初始化的变量是UB,0肯定是可以的。
  • 顺便说一句,实际上很多内存无论如何都是零。如果你对一大块内存进行简单的 malloc,它通常在某些系统上全为零,或者在其他系统上大多为​​零。
  • @NicholasWilson:对于大多数系统,只有当内存直接来自操作系统时(出于安全原因),这才是正确的。当它来自分配器缓存的内存时,它很可能包含它在 free()d 之前所做的任何事情。这也是为什么此类错误有时不会出现在测试用例/单元测试中的原因,而只是在“真实”程序运行一段时间后出现。 Valgrind 在这里进行救援。
  • 是的。这就是我的观点。如果您创建一个新变量并看到它为零,您不能立即假设您的程序中的某些东西已将其设置为零。由于大多数内存都准备好归零,因此它可能仍未初始化。我认为我的评论实际上比下面的大多数答案更接近于回答这个问题,因为提问者想知道为什么未初始化的内存可能为零。
  • 尝试分配一个数组,将所有项目设置为某个非零值,然后将其删除 [] 并再次分配它 - 只有第一个项目将被归零 - 其他项目保留它们的值 - 我认为这是一个特殊性内存分配器(在 Linux 下测试)。

标签: c++ memory-management


【解决方案1】:

有两个版本:

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的初始化规则解释new-initializer进行直接初始化。

#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]() 是否仍会将数组初始化为零,即使它具有专有的、完全不相关的实现?
  • @icepack:operator new 是用户(开发人员)定义的函数,它可以做任何想做的事情。该标准没有限制此功能的作用。就像 operator== 可以做任何用户定义的事情(它甚至不需要返回 bool 更不用说测试相等性了(尽管如果不这样做是不好的形式))。
  • 谢谢。明确一点:我的专有分配器的实现与零初始化无关,如果使用() 语法,缓冲区将被零初始化,与分配对象的类型及其构造函数没有任何关系? (而且我认为 C++11 在这方面没有任何改变)
  • 嗯...你确定 new unsigned int[5]() 将整数归零吗?
  • @J3STER 在新调用中传递一个指针称为放置新。见8.3.4 New。为什么要修改。因为我们正在强制值初始化。见11.6 Initializers。请注意,我们正在传递一个指向 new 运算符的指针。 wsa 的值没有改变,只是指向的地址改变了。
【解决方案2】:

operator new 不能保证将内存初始化为任何内容,并且在没有 new-initializer 的情况下分配 unsigned intnew-expression 会给对象留下一个不确定的值。

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

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

【讨论】:

    【解决方案3】:

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

    也有可能内存在以前的使用中只有 0。不要假设 delete 和 new 之间的内存没有发生任何事情。可能在后台做了一些你从未注意到的事情。此外,相同的指针值可能不是相同的物理内存。内存页面被移出和移入。指针可能被映射到与以前完全不同的位置。

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

    现代内存管理非常复杂,但作为 C++ 程序员,您并不真正关心(主要是‡)。遵守规则,你不会惹上麻烦的。

    ‡ 您可能会关心是否正在优化以减少页面错误。

    【讨论】:

      【解决方案4】:

      那不是operator new,那是new 运算符。其实差别很大!不同的是operator new是一个返回原始内存的函数;当您使用 new 运算符时,它会为您调用一个构造函数。设置 int 的值的是构造函数,而不是 operator new

      【讨论】:

      • 我认为正确的术语是“新表达式”,它调用了相应的新运算符。
      猜你喜欢
      • 2011-01-13
      • 2012-03-25
      • 2013-07-27
      • 1970-01-01
      • 1970-01-01
      • 2021-01-14
      • 1970-01-01
      • 2021-10-16
      相关资源
      最近更新 更多