第二个更重要:重用释放的指针可能是一个微妙的错误。您的代码继续正常工作,然后无缘无故崩溃,因为一些看似不相关的代码写入了重用指针恰好指向的内存中。
我曾经不得不在别人写的一个非常错误的程序上工作。我的直觉告诉我,许多错误与释放内存后继续使用指针的草率尝试有关。我修改了代码以在释放内存后将指针设置为 NULL ,并且bam,空指针异常开始出现。在我修复了所有的空指针异常之后,代码突然变得更加稳定了。
在我自己的代码中,我只调用我自己的函数,它是 free() 的包装器。它需要一个指向指针的指针,并在释放内存后使指针为空。在它调用 free 之前,它会调用Assert(p != NULL);
所以它仍然会捕获双重释放同一指针的尝试。
我的代码也做其他事情,例如(仅在 DEBUG 构建中)在分配内存后立即用明显的值填充内存,在调用之前做同样的事情free()
,以防有指针的副本等。 详细信息在这里。
编辑:根据请求,这里是示例代码。
void
FreeAnything(void **pp)
{
void *p;
AssertWithMessage(pp != NULL, "need pointer-to-pointer, got null value");
if (!pp)
return;
p = *pp;
AssertWithMessage(p != NULL, "attempt to free a null pointer");
if (!p)
return;
free(p);
*pp = NULL;
}
// FOO is a typedef for a struct type
void
FreeInstanceOfFoo(FOO **pp)
{
FOO *p;
AssertWithMessage(pp != NULL, "need pointer-to-pointer, got null value");
if (!pp)
return;
p = *pp;
AssertWithMessage(p != NULL, "attempt to free a null FOO pointer");
if (!p)
return;
AssertWithMessage(p->signature == FOO_SIG, "bad signature... is this really a FOO instance?");
// free resources held by FOO instance
if (p->storage_buffer)
FreeAnything(&p->storage_buffer);
if (p->other_resource)
FreeAnything(&p->other_resource);
// free FOO instance itself
free(p);
*pp = NULL;
}
注释:
您可以在第二个函数中看到我需要检查两个资源指针以查看它们是否不为空,然后调用FreeAnything()
. 这是因为assert()
那会抱怨空指针。我有这个断言是为了检测双重释放的尝试,但我认为它实际上并没有为我捕获很多错误;如果您想省略断言,那么您可以省略检查并始终调用FreeAnything()
. 除了断言之外,当您尝试释放空指针时不会发生任何不好的事情,FreeAnything()
因为它会检查指针并在它已经为空时返回。
我的实际函数名称更简洁,但我尝试为这个示例选择自记录名称。0xDC
此外,在我的实际代码中,我有只调试代码,它在调用之前用值填充缓冲区,free()
这样如果我有一个指向同一内存的额外指针(一个不会被清空的),那么数据变得非常明显它指向的是虚假数据。我有一个宏,DEBUG_ONLY()
,它在非调试版本中编译为空;FILL()
和一个对结构执行 a的宏sizeof()
。这两个工作同样好:sizeof(FOO)
或sizeof(*pfoo)
。所以这里是FILL()
宏:
#define FILL(p, b) \
(memset((p), b, sizeof(*(p)))
这是一个在调用之前使用FILL()
的示例:0xDC
if (p->storage_buffer)
{
DEBUG_ONLY(FILL(pfoo->storage_buffer, 0xDC);)
FreeAnything(&p->storage_buffer);
}
一个使用这个的例子:
PFOO pfoo = ConstructNewInstanceOfFoo(arg0, arg1, arg2);
DoSomethingWithFooInstance(pfoo);
FreeInstanceOfFoo(&pfoo);
assert(pfoo == NULL); // FreeInstanceOfFoo() nulled the pointer so this never fires