关于为什么在释放它们之后应该将指针设置为 NULL
似乎有两个论点。
Avoid crashing when double-freeing pointers.
简而言之:第二次意外调用 free()
,当它设置为 NULL
时不会崩溃。
这几乎总是掩盖逻辑错误,因为没有理由再次调用
free()
。让应用程序崩溃并能够修复它会更安全。不能保证会崩溃,因为有时会在同一地址分配新内存。
双重释放主要发生在有两个指针指向同一个地址时。
逻辑错误也可能导致数据损坏。
Avoid reusing freed pointers
简短:如果 malloc()
在同一位置分配内存,除非已释放指针设置为 NULL
如果偏移量足够大 (
someStruct->lastMember
,theArray[ someBigNumber]
).不会崩溃,而是数据损坏。将指针设置为
NULL
不能解决不同指针具有相同指针值的问题。
问题
这是 a post against blindly setting a pointer to NULL
after freeing .
- 哪个更难调试?
- 是否有可能同时捕获两者?
- 此类错误导致数据损坏而不是崩溃的可能性有多大?
请随意扩展这个问题。
最佳答案
第二个更重要:重新使用释放的指针可能是一个微妙的错误。您的代码保持正常工作,然后无缘无故地崩溃,因为一些看似无关的代码写入内存中,而重新使用的指针恰好指向该内存。
我曾经不得不处理别人编写的一个确实错误的程序。我的直觉告诉我,许多错误与释放内存后继续使用指针的草率尝试有关;我修改了代码以在释放内存后将指针设置为 NULL,然后 bam,空指针异常开始出现。在我修复了所有空指针异常之后,代码突然变得多稳定了。
在我自己的代码中,我只调用我自己的函数,它是 free() 的包装器。它接受一个指向指针的指针,并在释放内存后将指针置空。在它调用 free 之前,它会调用 Assert(p != NULL);
所以它仍然会捕获对同一指针的两次释放尝试。
我的代码也做了其他事情,例如(仅在 DEBUG 构建中)在分配内存后立即用一个明显的值填充内存,在调用 free()
之前做同样的事情以防有指针等的副本Details here.
编辑:根据请求,这里是示例代码。
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()
释放空指针时不会发生任何不良情况,因为它会检查指针并在它已经为空时返回。
我的实际函数名称更简洁,但我尝试为这个示例选择 self 记录的名称。此外,在我的实际代码中,我有仅调试代码,在调用 free()
之前用值 0xDC
填充缓冲区,这样如果我有一个额外的指针指向它内存(一个不会被清零的内存)很明显它指向的数据是虚假数据。我有一个宏,DEBUG_ONLY()
,它在非调试构建中编译为空;和一个在结构上执行 sizeof()
的宏 FILL()
。这两个同样有效: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
关于c - 释放它们后真的应该将指针设置为 `NULL` 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1879550/