Why does malloc produce seg fault when accessing a member reference from C++ struct?

Viewed 87

Consider the following code example:

#include <iostream>

struct Foo
{
    int x = 2;  
    int &rx = x;
};

int main()
{
    Foo *f1 = new Foo[4];
    std::cout<< f1[0].rx <<std::endl; //ok

    Foo *f2 = (Foo*) malloc (4 * sizeof(Foo));
    std::cout<< f2[0].rx <<std::endl; //memory leak
    
    free(f2);
    delete [] f1;
}

When new is used to allocate memory, the member reference rx is accessed normally and the correct value is printed. However, when malloc is used, accessing the member reference produces a segmentation fault. Can you please explain why?

I suspect a collision of C vs C++ implementation, but I'm not sure.

I ran the code with an address sanitizer which produced the following output:

ASAN:SIGSEGV
=================================================================
==32515==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x00000048cb69 sp 0x7ffea3d6bf40 bp 0x7ffea3d6bf70 T0)
    #0 0x48cb68 in main /home/.../Desktop/test.cpp:15
    #1 0x7f9861fb1544 in __libc_start_main (/lib64/libc.so.6+0x22544)
    #2 0x405578 (/home/.../Desktop/a.out+0x405578)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/.../Desktop/test.cpp:15 main
==32515==ABORTING
1 Answers

The reason of the SEGV is because the new operator calls the class default constructor, it is where the initialization of the non-static data members is done, in this case setting x to 2 and rx to x.

When you allocate the memory with malloc the default constructor is not called. So the SEGV rises because rx is never set to point to x, it is an undefined behavior.

You have to call the default constructor explicitly, with "new(f2) Foo", it is called placement new operator.

#include <iostream>
#include <malloc.h>

struct Foo
{
    int x = 2;  
    int &rx = x;
};

int main()
{
    Foo *f1 = new Foo[4];
    std::cout<< f1[0].rx <<std::endl; //ok

    Foo *f2 = (Foo*) malloc (4 * sizeof(Foo));
    new(f2) Foo;
    std::cout<< f2[0].rx <<std::endl; //memory leak
    
    free(f2);
    delete f1;
}

Here the placement new operator doesn't allocate memory it only calls the default constructor for the memory object allocated with malloc.

Now the result is what you expect.

2
2
Related