существует очень простой принцип - подпрограмма без памяти всегда должна совпадать, чтобы выделить подпрограмму.если мы используем несоответствие размещения и свободного времени, поведение может быть любым: все может быть случайным, нормально, или сбой по времени выполнения, или утечка памяти, или повреждение кучи.
если мы выделим память с выровненной версией оператора new
void* operator new ( std::size_t count, std::align_val_t al);
мы должны использовать соответствующую выровненную версию оператора delete
void operator delete ( void* ptr, std::align_val_t al );
вызов void operator delete ( void* ptr );
здесь всегда должен приводить к ошибке во время выполнения.давай просто протестируем
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
}
почему выровненная и не выровненная версия operator delete
всегда несовместима?давай подумаем - как можно выделить align на какое-то значение памяти?мы изначально всегда выделяем некоторый блок памяти.для возврата align указатель для использования - нам нужно отрегулировать выделенный указатель памяти для многократного выравнивания.Хорошо.это возможно, если выделить больше памяти, чем запрошено, и настроить указатель.но теперь вопрос - насколько свободен этот блок?вообще пользователь получил указатель не на начало выделенной памяти - как с этого указателя пользователя вернуться назад к началу выделенного блока?без дополнительной информации это невозможно.нам нужно сохранить указатель на фактически выделенную память, прежде чем пользователь вернет указатель.может быть, это будет более заметно в типичной реализации кода для выровненных new
и delete
использования _aligned_malloc
и _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);
}
прине выровнены new
и delete
используйте malloc
и free
void* operator new(size_t size)
{
return malloc(size);
}
void operator delete (void * p)
{
free(p);
}
теперь давайте посмотрим на внутреннюю реализацию _aligned_malloc
и _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]);
}
}
в общих словах _aligned_malloc
выделить size + sizeof(void*) + alignment - 1
вместо того, чтобы запрашивать вызывающий size
.отрегулируйте выделенный указатель в соответствии с выравниванием, и сохраните первоначально выделенную память, прежде чем указатель вернется к вызывающей стороне.
и _aligned_free(pv)
вызывают не free(pv)
, а free(((void**)pv)[-1]);
- для всегда другой указатель.потому что этот эффект _aligned_free(pv)
всегда другое сравнение free(pv)
.и operator delete(pv, al);
всегда несовместимы с operator delete(pv);
, если, скажем, delete []
обычно имеют тот же эффект, что и delete
, но выравнивание и не выравнивание всегда отличаются по времени выполнения.