删除导致调试断言的指针
Deleting Pointer Causing Debug Assert
我一直在研究内存池分配器 class,没有出现重大问题,没有 Visual Studio 的调试断言 (_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) ) 每当我试图用 delete
.
释放由 new
分配的内存时被抛出
typedef uintptr_t uptr;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
struct FreeList
{
FreeList *next;
};
template<class T, u8 alignment, u32 poolSize>
class PoolAllocator
{
private:
u8 _paddedSize; // The size in bytes of each allocated chunk of memory.
u32 _numAllocations;// The number of allocations made by the pool.
u32 _freeBytes; // The number of bytes left in the pool.
u32 _usedBytes; // The number of bytes currently occupied in the pool.
FreeList* _freeListHead; // A pointer to a freed space in memory.
public:
PoolAllocator() :
_paddedSize((sizeof(T) > sizeof(uptr) ? sizeof(T) : sizeof(uptr))),
_numAllocations(0),
_freeBytes(0),
_usedBytes(0),
_freeListHead(nullptr)
{
_freeListHead = reinterpret_cast<FreeList*>(operator new (_paddedSize * poolSize));
_freeBytes = _paddedSize * poolSize;
uptr current = reinterpret_cast<uptr>(_freeListHead);
uptr last = current + (_paddedSize * poolSize);
for (int i = 0; i < poolSize-1; i++)
{
uptr next = current + _paddedSize;
(reinterpret_cast<FreeList*>(current))->next = reinterpret_cast<FreeList*>(next);
current += _paddedSize;
}
reinterpret_cast<FreeList*>(current)->next = nullptr;
}
T *allocate()
{
if (_freeListHead != nullptr && _freeBytes >= _paddedSize) // Make sure the pool has memory left
{
uptr *toReturn = reinterpret_cast<uptr*>(_freeListHead); // Cast the pointer to a modifiable data type.
_freeListHead = _freeListHead->next; // VITAL THAT THIS IS BEFORE SETTING DATA TO 0.
*toReturn = 0; // Set the data at the memory location to 0.
_freeBytes -= _paddedSize;
_usedBytes += _paddedSize;
_numAllocations++;
printf("Allocated %d bytes of memory at %p.\n", _paddedSize, toReturn);
return reinterpret_cast<T*>(toReturn);
}
else
{
printf("Pool allocator out of memory! Returning nullptr.\n");
return nullptr;
}
}
void free(T **ptr)
{
FreeList *newHead = reinterpret_cast<FreeList*>(*ptr);
*ptr = nullptr;
newHead->next = _freeListHead;
_freeListHead = newHead;
_freeBytes += _paddedSize;
_usedBytes -= _paddedSize;
_numAllocations--;
printf("Freed %d bytes of memory at %p.\n", _paddedSize, _freeListHead);
}
void clear()
{
assert(_usedBytes == 0);
FreeList *head = _freeListHead;
while (head != 0)
{
FreeList *next = head->next;
delete reinterpret_cast<T*>(head);
head = next;
}
_paddedSize = 0;
_numAllocations = 0;
_freeBytes = 0;
_usedBytes = 0;
_freeListHead = nullptr;
}
};
我使用的测试代码:
int main()
{
PoolAllocator<int, 4, 4> pool;
int *a, *b, *c, *d, *e;
a = pool.allocate();
b = pool.allocate();
c = pool.allocate();
d = pool.allocate();
pool.free(&a);
e = pool.allocate();
printf("A | %p\t%d\nB | %p\t%d\nC | %p\t%d\nD | %p\t%d\nE | %p\t%d\n", a, 0, b, *b, c, *c, d, *d, e, *e);
pool.free(&b);
pool.free(&c);
pool.free(&d);
pool.free(&e);
pool.clear();
return 0;
}
问题所在:
void clear()
{
assert(_usedBytes == 0);
FreeList *head = _freeListHead;
while (head != 0)
{
FreeList *next = head->next;
delete reinterpret_cast<T*>(head); // Debug assert
head = next;
}
_paddedSize = 0;
_numAllocations = 0;
_freeBytes = 0;
_usedBytes = 0;
_freeListHead = nullptr;
}
这段代码应该做的是通过内存位置的链接列表递增,其中存储了类型 T 的各个数据片段。我认为这是一段有效代码的原因是,因为最初分配的内存被分成 sizeof(T)
大小的片段。因此,我假设将内存地址类型转换为 T*
类型的指针是合适的,这样单个内存块将被完全释放。这将对整个链表完成,确保释放所有分配的内存。但是,当我 运行 代码时,它总是在删除时抛出断言。
单步执行代码显示,在第一次迭代类型转换和 delete
ing 之后,指针呈现奇怪的值(next
变量,例如:
next 0x004b2864 {next=0xfeeefeee {next=??? } } FreeList *
而以前是
next 0x006c2864 {next=0x006c2860 {next=0x006c285c {next=0x00000000
} } } FreeList *
正如它应该的那样)。我以多种不同的方式多次尝试过这种方法,包括将类型转换为 void*
而不是 T*
.
这个问题已经困扰我好几天了,如有任何帮助,我们将不胜感激。谢谢。
这不会编译:
struct FreeList
{
FreeList *next;
};
...但以下将:
struct FreeList
{
struct FreeList *next;
};
否则,编译器会抛出结构定义不完整或类型未知的错误FreeList
。
reinterpret_cast<T*>(head)->~T();
我测试了这个并且不再通过替换 delete reinterpret_cast(head);
得到断言
你不能调用 delete on head 因为内存不是通过调用 new T() 分配的;
我一直在研究内存池分配器 class,没有出现重大问题,没有 Visual Studio 的调试断言 (_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) ) 每当我试图用 delete
.
new
分配的内存时被抛出
typedef uintptr_t uptr;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
struct FreeList
{
FreeList *next;
};
template<class T, u8 alignment, u32 poolSize>
class PoolAllocator
{
private:
u8 _paddedSize; // The size in bytes of each allocated chunk of memory.
u32 _numAllocations;// The number of allocations made by the pool.
u32 _freeBytes; // The number of bytes left in the pool.
u32 _usedBytes; // The number of bytes currently occupied in the pool.
FreeList* _freeListHead; // A pointer to a freed space in memory.
public:
PoolAllocator() :
_paddedSize((sizeof(T) > sizeof(uptr) ? sizeof(T) : sizeof(uptr))),
_numAllocations(0),
_freeBytes(0),
_usedBytes(0),
_freeListHead(nullptr)
{
_freeListHead = reinterpret_cast<FreeList*>(operator new (_paddedSize * poolSize));
_freeBytes = _paddedSize * poolSize;
uptr current = reinterpret_cast<uptr>(_freeListHead);
uptr last = current + (_paddedSize * poolSize);
for (int i = 0; i < poolSize-1; i++)
{
uptr next = current + _paddedSize;
(reinterpret_cast<FreeList*>(current))->next = reinterpret_cast<FreeList*>(next);
current += _paddedSize;
}
reinterpret_cast<FreeList*>(current)->next = nullptr;
}
T *allocate()
{
if (_freeListHead != nullptr && _freeBytes >= _paddedSize) // Make sure the pool has memory left
{
uptr *toReturn = reinterpret_cast<uptr*>(_freeListHead); // Cast the pointer to a modifiable data type.
_freeListHead = _freeListHead->next; // VITAL THAT THIS IS BEFORE SETTING DATA TO 0.
*toReturn = 0; // Set the data at the memory location to 0.
_freeBytes -= _paddedSize;
_usedBytes += _paddedSize;
_numAllocations++;
printf("Allocated %d bytes of memory at %p.\n", _paddedSize, toReturn);
return reinterpret_cast<T*>(toReturn);
}
else
{
printf("Pool allocator out of memory! Returning nullptr.\n");
return nullptr;
}
}
void free(T **ptr)
{
FreeList *newHead = reinterpret_cast<FreeList*>(*ptr);
*ptr = nullptr;
newHead->next = _freeListHead;
_freeListHead = newHead;
_freeBytes += _paddedSize;
_usedBytes -= _paddedSize;
_numAllocations--;
printf("Freed %d bytes of memory at %p.\n", _paddedSize, _freeListHead);
}
void clear()
{
assert(_usedBytes == 0);
FreeList *head = _freeListHead;
while (head != 0)
{
FreeList *next = head->next;
delete reinterpret_cast<T*>(head);
head = next;
}
_paddedSize = 0;
_numAllocations = 0;
_freeBytes = 0;
_usedBytes = 0;
_freeListHead = nullptr;
}
};
我使用的测试代码:
int main()
{
PoolAllocator<int, 4, 4> pool;
int *a, *b, *c, *d, *e;
a = pool.allocate();
b = pool.allocate();
c = pool.allocate();
d = pool.allocate();
pool.free(&a);
e = pool.allocate();
printf("A | %p\t%d\nB | %p\t%d\nC | %p\t%d\nD | %p\t%d\nE | %p\t%d\n", a, 0, b, *b, c, *c, d, *d, e, *e);
pool.free(&b);
pool.free(&c);
pool.free(&d);
pool.free(&e);
pool.clear();
return 0;
}
问题所在:
void clear()
{
assert(_usedBytes == 0);
FreeList *head = _freeListHead;
while (head != 0)
{
FreeList *next = head->next;
delete reinterpret_cast<T*>(head); // Debug assert
head = next;
}
_paddedSize = 0;
_numAllocations = 0;
_freeBytes = 0;
_usedBytes = 0;
_freeListHead = nullptr;
}
这段代码应该做的是通过内存位置的链接列表递增,其中存储了类型 T 的各个数据片段。我认为这是一段有效代码的原因是,因为最初分配的内存被分成 sizeof(T)
大小的片段。因此,我假设将内存地址类型转换为 T*
类型的指针是合适的,这样单个内存块将被完全释放。这将对整个链表完成,确保释放所有分配的内存。但是,当我 运行 代码时,它总是在删除时抛出断言。
单步执行代码显示,在第一次迭代类型转换和 delete
ing 之后,指针呈现奇怪的值(next
变量,例如:
next 0x004b2864 {next=0xfeeefeee {next=??? } } FreeList *
而以前是
next 0x006c2864 {next=0x006c2860 {next=0x006c285c {next=0x00000000 } } } FreeList *
正如它应该的那样)。我以多种不同的方式多次尝试过这种方法,包括将类型转换为 void*
而不是 T*
.
这个问题已经困扰我好几天了,如有任何帮助,我们将不胜感激。谢谢。
这不会编译:
struct FreeList
{
FreeList *next;
};
...但以下将:
struct FreeList
{
struct FreeList *next;
};
否则,编译器会抛出结构定义不完整或类型未知的错误FreeList
。
reinterpret_cast<T*>(head)->~T();
我测试了这个并且不再通过替换 delete reinterpret_cast(head);
得到断言你不能调用 delete on head 因为内存不是通过调用 new T() 分配的;