Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to invoke aligned new/delete properly?

How do I call new operator with alignment?

auto foo = new(std::align_val_t(32)) Foo; //?

and then, how to delete it properly?

delete(std::align_val_t(32), foo); //?

If this is the right form of using these overloads, why valgring complaining about mismatched free()/delete/delete[]?

like image 633
kreuzerkrieg Avatar asked Dec 25 '18 12:12

kreuzerkrieg


People also ask

How delete operator works in c++?

When delete is used to deallocate memory for a C++ class object, the object's destructor is called before the object's memory is deallocated (if the object has a destructor). If the operand to the delete operator is a modifiable l-value, its value is undefined after the object is deleted.

How does operator delete work?

Delete is an operator that is used to destroy array and non-array(pointer) objects which are created by new expression. New operator is used for dynamic memory allocation which puts variables on heap memory. Which means Delete operator deallocates memory from heap.

What is memory alignment C++?

Alignment refers to the arrangement of data in memory, and specifically deals with the issue of accessing data as proper units of information from main memory. First we must conceptualize main memory as a contiguous block of consecutive memory locations. Each location contains a fixed number of bits.

Does malloc return aligned memory?

Since malloc (or another dynamic memory allocator) is not necessarily guaranteed to align memory as we require, we'll need to perform two extra steps: Request extra bytes so we can returned an aligned address. Request extra bytes and store the offset between our original pointer and our aligned pointer.


1 Answers

exist very basic principle - the memory free routine always must match to allocate routine. if we use mismatch allocate and free - run time behavior can be any: all can be random ok, or crash by run-time, or memory leak, or heap corruption.

if we allocate memory with aligned version of operator new

void* operator new  ( std::size_t count, std::align_val_t al);

we must use the corresponding aligned version of operator delete

void operator delete  ( void* ptr, std::align_val_t al );

call void operator delete ( void* ptr ); here always must lead to run-time error. let simply test

    std::align_val_t al = (std::align_val_t)256;
    if (void* pv = operator new(8, al))
    {
        operator delete(pv, al);
        //operator delete(pv); this line crash, or silently corrupt heap
    }

why is aligned and not aligned version of operator delete always incompatible ? let think - how is possible allocate align on some value memory ? we initially always allocate some memory block. for return align pointer to use - we need adjust allocated memory pointer to be multiple align. ok. this is possible by allocate more memory than requested and adjust pointer. but now question - how free this block ? in general user got pointer not to the begin of allocated memory - how from this user pointer jump back to begin of allocated block ? without additional info this is impossible. we need store pointer to actual allocated memory before user returned pointer. may be this will be more visible in code typical implementation for aligned new and delete use _aligned_malloc and _aligned_free

void* operator new(size_t size, std::align_val_t al)
{
    return _aligned_malloc(size, static_cast<size_t>(al));
}

void operator delete  (void * p, std::align_val_t al)
{
    _aligned_free(p);
}

when not aligned new and delete use malloc and free

void* operator new(size_t size)
{
    return malloc(size);
}

void operator delete  (void * p)
{
    free(p);
}

now let look for internal implementation of _aligned_malloc and _aligned_free

void* __cdecl _aligned_malloc(size_t size, size_t alignment)
{
    if (!alignment || ((alignment - 1) & alignment))
    {
        // alignment is not a power of 2 or is zero
        return 0;
    }

    union {
        void* pv;
        void** ppv;
        uintptr_t up;
    };

    if (void* buf = malloc(size + sizeof(void*) + --alignment))
    {
        pv = buf;
        up = (up + sizeof(void*) + alignment) & ~alignment;
        ppv[-1] = buf;

        return pv;
    }

    return 0;
}

void __cdecl _aligned_free(void * pv)
{
    if (pv)
    {
        free(((void**)pv)[-1]);
    }
}

in general words _aligned_malloc allocate size + sizeof(void*) + alignment - 1 instead requested by caller size. adjust allocated pointer to fit alignment , and store originally allocated memory before pointer returned to caller.

and _aligned_free(pv) call not free(pv) but free(((void**)pv)[-1]); - for always another pointer. because this effect of _aligned_free(pv) always another compare free(pv). and operator delete(pv, al); always not compatible with operator delete(pv); if say delete [] usual have the same effect as delete but align vs not align always run time different.

like image 86
RbMm Avatar answered Sep 23 '22 15:09

RbMm