88
votes

There is such code:

#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;
}

Result:

wsk2: 0x928e008 5
wsk2: 0x928e008 0

I have read that new doesn't initialize memory with zeroes. But here it seems that it does. How does it work?

4
It doesn't, you'd have to use new unsigned int(). Reading uninitialized variables is UB, a 0 is certainly possible. - Hans Passant
As an aside, in practice lots of memory is zero anyway. If you do a plain malloc of a large chunk of memory, it's often all zero on some systems, or mostly zero on others. - Nicholas Wilson
@NicholasWilson: For most systems, this is only true when the memory came directly from the OS (for security reasons). When it came from memory cached by the allocator, it most likely contains whatever it did before it was free()d. This is also a reason why such bugs sometimes do not arise in testcases/unit tests, but only after a while of the "real" program running. Valgrind to the rescue here. - PlasmaHH
Yes. That's kind of my point. If you make a new variable and see that's it's zero, you can't straight away assume that something within your program has set it to zero. Since most memory comes ready-zeroed, it's probably still uninitialised. I think my remark is actually closer to answering the question than most of the answers below, because the questioner wanted to know why uninitialised memory might be zero. - Nicholas Wilson
Try allocating an array, setting all items to some nonzero value, then delete[] it and allocate it again - only the first item will be zeroed - others retain their values - which I think is a pecurliarity of the memory allocator (tested under Linux). - kyku

4 Answers

220
votes

There are two versions:

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

Also works for arrays:

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

In answer to comment below.

Ehm... are you sure that new unsigned int[5]() zeroes the integers?

Apparently yes:

[C++11: 5.3.4/15]: A new-expression that creates an object of type T initializes that object as follows: If the new-initializer is omitted, the object is default-initialized (8.5); if no initialization is performed, the object has indeterminate value. Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 for direct-initialization.

#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";
}

Results:

> 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
>
21
votes

operator new is not guaranteed to initialize memory to anything, and the new-expression that allocates an unsigned int without a new-initializer leaves the object with an indeterminate value.

Reading the value of an uninitialized object results in undefined behavior. Undefined behavior includes evaluating to the value zero with no ill effects but could result in anything happening so you should avoid causing it.

In C++11, the language used is that the allocated objects are default-initialized which for non-class types means that no initialization is performed. This is different from the meaning of default-initialized in C++03.

4
votes

With some compilers, the debug version of new will initialise the data, but there is certainly nothing that you can rely on.

It is also possible that the memory just had 0 from a previous use. Don't assume that nothing happened to the memory between delete and new. There could be something done in the background that you never noticed. Also, the same pointer value might not be the same physical memory. Memory pages get moved and paged out and in. A pointer might be mapped to an entirely different location than earlier.

Bottom line: if you didn't specifically initialise a memory location then you can assume nothing about its contents. The memory manager might not even allocate a specific physical memory location until you use the memory.

Modern memory management is amazingly complex, but as a C++ programmer you don't really care (mostly‡). Play by the rules and you won't get into trouble.

‡ You might care if you are optimising to reduce page faults.

3
votes

That's not operator new, that's the new operator. There's actually a big difference! The difference is that operator new is a function that returns raw memory; when you use the new operator, it invokes a constructor for you. It's the constructor that's setting the value of that int, not operator new.