Home > Software engineering >  Allocating memory thorugh malloc for std::string array not working
Allocating memory thorugh malloc for std::string array not working

Time:08-16

I'm having trouble allocating memory on the heap for an array of strings. Allocating with new works but malloc segfaults each time. The reason I want to use malloc in the first place is that I don't want to call the constructor unnecessarily.

This works fine

std::string* strings = new std::string[6];

This doesn't

std::string* strings = (std::string *)malloc(sizeof(std::string[6]));

One thing I've noticed is that the first variant (using new) allocates 248 bytes of memory while the second allocates only 240. This 8 byte difference is constant no matter the size of the array from what I've gathered and I cannot find what the source of the difference is.

Here's the whole code that segfaults.

#include <iostream>

void* operator new(size_t size)
{
    std::cout << size << std::endl;
    return malloc(size);
}

void* operator new [](size_t size)
{
    std::cout << size << std::endl;
    return malloc(size);
}

int main() {
    std::string* strings = new std::string[6];
    strings = (std::string *)malloc(sizeof(std::string[6]));

    strings[0] = std::string("test");

    return 0;
}

Another thing I've noticed is that the above code seems to work if I use memset after malloc to set all of the bytes I allocated with malloc to 0. I don't understand where the 8 byte difference comes from if this works and also why this variant works at all. Why would it work just because I set all of the bytes to 0?

CodePudding user response:

malloc() only allocates raw memory, but it does not construct any objects inside of that memory.

new and new[] both allocate memory and construct objects.

If you really want to use malloc() to create an array of C objects (which you really SHOULD NOT do!), then you will have to call the object constructors yourself using placement-new, and also call the object destructors yourself before freeing the memory, eg:

std::string* strings = static_cast<std::string*>(
    malloc(sizeof(std::string) * 6)
);

for(int i = 0; i < 6;   i) {
    new (&strings[i]) std::string;
}

...

for(int i = 0; i < 6;   i) {
    strings[i].~std::string();
}

free(strings);

In C 11 and C 14, you should use std::aligned_storage to help calculate the necessary size of the array memory, eg:

using string_storage = std::aligned_storage<sizeof(std::string), alignof(std::string)>::type;

void *buffer = malloc(sizeof(string_storage) * 6);

std::string* strings = reinterpret_cast<std::string*>(buffer);

for(int i = 0; i < 6;   i) {
    new (&strings[i]) std::string;
}

...

for(int i = 0; i < 6;   i) {
    strings[i].~std::string();
}

free(buffer);

In C 17 and later, you should use std::aligned_alloc() instead of malloc() directly, eg:

std::string* strings = static_cast<std::string*>(
    std::aligned_alloc(alignof(std::string), sizeof(std::string) * 6)
);

for(int i = 0; i < 6;   i) {
    new (&strings[i]) std::string;
}

...

for(int i = 0; i < 6;   i) {
    strings[i].~std::string();
}

std::free(strings);

CodePudding user response:

Allocating via new means the constructor is run. Please always use new and delete with C classes (and std::string is a C class), whenever you can.

When you do malloc() / free(), only memory allocation is done, constructor (destructor) is not run. This means, the object is not initialized. Technically you might still be able to use placement new (i.e., new(pointer) Type) to initialize it, but it's better and more conformant to use classic new.

If you wanted to allocate multiple objects, that's what containers are for. Please use them. Multiple top-grade engineers work on std::vector<>, std::array<>, std::set<>, std::map<> to work and be optimal - it's very hard to beat them in performance, stability or other metrics and, even if you do, the next coder at the same company needs to learn into your specific data structures. So it's suggested not to use custom and locally implemented allocations where a container could be used, unless for a very strong reason (or, of course, didactic purposes).

  • Related