Dov
Dov

Reputation: 8572

traversing memory owned by a unique_ptr gives segfault

In order not to have to remember to delete, we are using unique_ptr to manage the memory. We were under the impression that we can write and read in the memory, just that deletion is up to the smart pointer. However, the following code crashes on i=7220 with a segfault.

What is wrong?

#include <memory>
using namespace std;

int main() {
  const uint32_t n = 40000000;
  uint64_t*p = new uint64_t[n]; // this works, but we have to delete the memory or leak
  for (int i = 0; i < n; i++)
    p[i] = i;

  unique_ptr<uint64_t> mem = make_unique<uint64_t>(n);

  uint64_t* p1 = mem.get();
  for (int i = 0; i < n; i++) // this crashes at i=7220
    p1[i] = i;

  return 0;
}

Upvotes: 2

Views: 185

Answers (3)

Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 29985

Aside from the fix other answers mentioned, you likely don't want to use make_unique here. make_unique will zero-out every single element (40 million ints!) and you immediately overwrite them. Your options are to use make_unique_for_overwrite which requires C++20, or to use new:

#include <cstdint>  // for uint64_t
#include <memory>   // for unique_ptr, unique_ptr<>::pointer
#include <numeric>  // for iota

int main() {
  auto const n = 40'000'000;
  auto const p = std::unique_ptr<std::uint64_t[]>{new std::uint64_t[n]};
  std::iota(p.get(), p.get() + n, 0);
}

Upvotes: 2

Ted Lyngmo
Ted Lyngmo

Reputation: 117658

unique_ptr<uint64_t> mem = make_unique<uint64_t>(n);

This allocates one uint64_t dynamically with the value n.

You want:

unique_ptr<uint64_t[]> mem = make_unique<uint64_t[]>(n);

This specialization allocates an array of uint64_t with n elements, and has an operator[] overload which makes the below possible:

for (int i = 0; i < n; i++)
    mem[i] = i;

So, there's no need to do uint64_t* p1 = mem.get();

Upvotes: 10

user7860670
user7860670

Reputation: 37578

unique_ptr<uint64_t> is a pointer to a single uint64_t value. In order to make it store an array you need to use array of unknown bound syntax:

unique_ptr<uint64_t[]> mem = make_unique<uint64_t[]>(n);

or just

auto mem{make_unique<uint64_t[]>(n)};

Note that this variant overloads operator [] so there is no need to create an intermediate raw pointer uint64_t* p1 = mem.get();

Upvotes: 5

Related Questions