1

I have recently gotten into C++ and I've encountered a problem working with malloc. The code below does not print out "Success" (Program crashes with exit code 0xC0000005) whereas if I use calloc instead everything works fine.

int main(){
    std::string* pointer = (std::string*) malloc(4 * sizeof(std::string));

    for(int i = 0; i < 4; i++){
        pointer[i] = "test";
    }

    std::cout << "Success" << std::endl;

    return 0;
}

The code below works.

calloc(4, sizeof(std::string));

Malloc also works if I allocate like 12 times the normal amount.

Can somebody explain this behavior ? Does this have something to do with std::string ?

TinSlam
  • 11
  • 6
  • 3
    Consider reading a [good book](https://stackoverflow.com/a/388282). – Passer By Aug 12 '18 at 12:06
  • 7
    Don't use `malloc`/`calloc`/`realloc`/`free` in C++. Use `new`/`new[]`/`delete`/`delete` that properly call constructors/destructors. Or even better, use containers and/or smart pointers. Avoid manual memory management. – Jesper Juhl Aug 12 '18 at 12:11
  • 1
    The issues is that you are not constructing the strings which is needed.....If you have your heart set on using `malloc` you need to placement `new` the memory you get back before you use it as a string...... – DarthRubik Aug 12 '18 at 12:13
  • But seriously, just use a normal `new` – DarthRubik Aug 12 '18 at 12:14
  • As a side note.....no I don't know why when using `calloc` it "works"......but it is UB what happens if you use an object without constructing it......(ie it could do anything: segfault, reformat your harddrive, and most dangerously silently work)......My guess is somewhere inside the `std::string` there is a pointer, and if it is null it will allocate memory on assignment, but if it isn't null it assumes it is a valid pointer (which is not going to be the case) – DarthRubik Aug 12 '18 at 12:17
  • Thanks for the help guys. New works just fine. As for the calloc I think it initializes the string itself so I don't need to construct it manually. – TinSlam Aug 12 '18 at 12:20
  • 4
    ***I think it initializes the string itself*** It does not construct the string. The way you are using it is UB. – drescherjm Aug 12 '18 at 12:45
  • 4
    @TinSlam *I have recently gotten into C++* -- What books have you been reading since you are new to C++? No book that I know of introduces a C++ programmer to `malloc` or `calloc` in creating objects. Or are you reading `C` books and applying it to C++? If so, you will make more errors similar to the ones you're making now. – PaulMcKenzie Aug 12 '18 at 13:12
  • Possible duplicate of https://stackoverflow.com/questions/7609981/possible-memory-leak-with-malloc-struct-stdstring-and-free – Galik Aug 12 '18 at 13:48
  • @DarthRubik "*I don't know why when using `calloc` it "works"......*" - because it zeros out the memory it allocates, whereas `malloc()` doesn't. – Remy Lebeau Aug 12 '18 at 18:33
  • @RemyLebeau I was saying that I don't know enough about the innards of a `std::string` to make anything more than an educated guess....not that I did not understand the difference between `calloc` and `malloc` – DarthRubik Aug 13 '18 at 23:53

2 Answers2

8
std::string* pointer = (std::string*) malloc(4 * sizeof(std::string)); 

This only allocates memory sufficient to hold 4 string objects. It does not construct them, and any use before construction is undefined.

EDIT: As for why it works with calloc: most likely, the default constructor of std::string sets all fields to zero. Probably calloc simply happens to do the same as std::string default construction on your system. In contrast, small malloc() objects are probably allocated with an initial junk, so the objects are not in a valid state

With a big enough malloc() the effect is similar to calloc(). When malloc() can't reuse previously allocated block (with potential junk) it requests new blocks from the operating system. Usually the OS will clear any block it hands to the application (to avoid information leak), making big malloc() behave like calloc().

This won't work on all systems and compilers. It depends on how the compiler implements std::string and depends on how undefined behavior may confuse the compiler. This means that if it works on your compiler today, it might not work on a different system, or with a newer compiler. Worse, it might stop working on your system with your compiler, after you edit seemingly unrelated code in your program. Never rely on undefined behavior.

The simplest solution is to let C++ deal with allocation and construction, and later with destruction and deallocation. This automatically done by

std::vector<std::string> str_vec(4);

If you insist on allocating and deallocating your own memory (which is a bad idea 99.9% of the time), you should use new rather than malloc. Unlike malloc(), using new actually constructs the objects.

// better use std::unique_ptr<std::string[]>
// since at least it automatically
// destroys and frees the objects.
std::string* pointer = new std::string[4];

... use the pointer ...

// better to rely on std::unique_ptr to auto delete the thing.
delete [] pointer;

If for some bizarre reason you still want to use malloc (which is a bad idea 99.99% of the time), you must construct and destruct the objects by yourself:

constexpr int size = 4;
std::string* pointer = (std::string*) malloc(size * sizeof(std::string)); 
for (int i=0; i != size ;++i)
    // placement new constructs the strings
    new (pointer+i) std::string;

... use the pointer ....

for (int i=0; i != size ;++i)
    // destruct the strings
    pointer[i].~string();
free(pointer);
Michael Veksler
  • 8,217
  • 1
  • 20
  • 33
5

Can somebody explain this behavior ?

The behaviour is undefined in both cases. The calloc case appearing to work is simply due to bad luck.

In order to use an object in allocated space of memory, you must first construct the object. You never constructed any string objects.

Simplest way to construct a dynamically allocated array of objects is to use a vector:

std::vector<std::string> vec(4);
Mgetz
  • 5,108
  • 2
  • 33
  • 51
eerorika
  • 232,697
  • 12
  • 197
  • 326