0

Let's say I defined a struct that had an array of pointers to objects inside it, like so:

struct node {
    node *arr[10];
};

Then, I initialize an instance of the struct on the heap and check the contents of its array of pointers.

node *curr = new node;
for (int i = 0; i < 10; ++i) {
    if (curr->arr[i] == nullptr) std::cout << "null" << std::endl; 
} // this would print "null" 10 times in my tests in online IDEs and on QTCreator's C++ environment.

However, why do I see that each of the elements of the array is a nullptr? Shouldn't it be a garbage value? Is this default behavior for all structs with arrays of pointers?

I thought they should just be garbage pointers, not all nullptrs. Any tips on how to think about initialization of the node instance would be awesome! Thank you!

2
  • 1
    "Garbage" really means "indeterminate" - they might, by chance, happen to be null pointers. (Indeed many OSes zero out memory when first providing it to a process, so if you are getting fresh memory from the OS, instead of memory reused from a previous delete, you are more likely to see zeros. Never any guarantees!) Commented May 13, 2021 at 3:10
  • stackoverflow.com/questions/35666894/garbage-characters-in-c/… explains it pretty well - for characters, not pointers, but the principle is exactly the same. Commented May 13, 2021 at 3:13

1 Answer 1

0

It depends on the memory allocator's status, it's a black box for programmers . For your code snippet, we can check the corresponding assembly:

f():                                  # @f()
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     edi, 80
        call    operator new(unsigned long)
        mov     qword ptr [rbp - 8], rax
        mov     dword ptr [rbp - 12], 0

Only the operator new is called, since the struct is a plain old data type, we don't have a non-trivial constructor called here, the memory we allocated is untouched.

If we change the code slightly by inserting a memory allocation and deallocation with exactly same size with your struct:

#include <cstring>
#include <iostream>
#include <memory>
const size_t n = 10;
struct node {
  node *arr[n];
};

void f() {
  {
    std::unique_ptr<char[]> up(new char[sizeof(node)]);
    memset(up.get(), 0xff, sizeof(node));
  }

  node *curr = new node;
  for (int i = 0; i < n; ++i) {
    if (curr->arr[i] != nullptr) std::cout << "not null" << std::endl;
  }
}

int main(int argc, char *argv[]) {
  f();
  return 0;
}

We get garbage values now, this is because that we have allocated a memory block with the size sizeof(node), and fill it with value then deallocate it immediately.

For modern memory allocators, they tend to retain a memory pool for different size ranges to boost the performance by avoiding too frequent system call (You may reference the design for jemalloc, the one in libc is similar but much simpler). In my code, we allocated a memory block with the size sizeof(node) and deallocate it, the memory will be held by the allocator. Then we allocate a node with node *curr = new node;, it's likely that we will get the same address returned by the previous allocation. So we get the expected garbage value now, it will be a different case for complex programs with multiple threads, we will ignore the complex scenario here.

To initialize variable in c++ is a good habit and can avoid many related bugs, I suggest always do it.

Online demo.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.