0

I cannot understand why this code segfault. I allocate enough space for n element of a giving type with an allocator, and then fill the space with a copy of the default constructed type with std::fill.

#define TESTED_TYPE std::string

size_t n = 5;
std::allocator<TESTED_TYPE> my_alloc;
TESTED_TYPE *data = my_alloc.allocate(n);
TESTED_TYPE val = TESTED_TYPE();
std::fill(data, data + n, val);

This code compile fine and doesnt crash with basic type like int, char etc... but not with std::string. If I give to std::fill a not empty string the code doesnt segfault either. Why ?

bperraud
  • 1
  • 1
  • 1

1 Answers1

2

You never invoked the std::string constructor, so the memory pointed to by data is uninitialized garbage.

std::fill is (probably) implemented a loop that does *ptr = value; for every element in the range you pass. std::string::operator= must make sure that any memory it was previously holding on to is freed, so it calls delete[] on its internal buffer. Since this buffer is just an arbitrary address, this will very often access unmapped memory and crash.

To fix this, you need to also invoke the placement new operator on each of your freshly allocated array elements so that their internal state is consistent and safe to use.

Botje
  • 26,269
  • 3
  • 31
  • 41
  • If I new() every element, and delete them later wouldn't the problem be there too ? The delete[] would try to delete an empty internal buffer and crash ? – bperraud Dec 02 '22 at 15:07
  • placement new will adjust `data[i]` such that it is safe to call `delete &data[i]`, which includes either setting the internal buffer to a nullpointer or to a valid memory buffer allocated with `new[]`. – Botje Dec 02 '22 at 15:08
  • so the construction of an object is different if you allocate on the stack rather than on the heap ? Why is this the case ? – bperraud Dec 02 '22 at 15:13
  • 2
    How did you infer that from my question or my comment? You did the C++ equivalent of `std::string *data = (std::string *) malloc(n * sizeof(std::string));`, which is [guaranteed to go wrong for non-POD types](https://stackoverflow.com/questions/184537/in-what-cases-do-i-use-malloc-and-or-new) – Botje Dec 02 '22 at 15:16
  • 1
    @bperraud The construction is the same, but there is a difference between allocating memory and creating an object; you're only doing the former and need to use `new` for creating objects in the allocated memory. – molbdnilo Dec 02 '22 at 15:33
  • but I do call the default constructor explicitly just before the call to fill so how isn't the object constructed ? If the constructor is indead call then why isn't he constructed right like a new() call ? – bperraud Dec 02 '22 at 15:41
  • @bperraud - The `val` object is constructed, but nothing is constructed at `*data`. Note that the allocator might have `construct` and `destroy` members in addition to `allocate`. It is a two step process. – BoP Dec 02 '22 at 16:44
  • I know how to fix this but can't yet understand it... my initial question was why do I need to construct the object ONLY IF the string is empty at the beginning ? An none empty string doesn't need to be constructed first in the allocated memory. As Botje suggest this might come from the deletion of the previous element at data, so the crash should happen not depending on the value that is passed to fill right ? – bperraud Dec 02 '22 at 18:10
  • You are completely at the mercy of what uninitialized memory was there before. That it happens with a non-empty string is pure luck. – Botje Dec 02 '22 at 18:50