本文介绍了在释放它们之后,真的应该将指针设置为“NULL"吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

似乎有两个论点为什么在释放它们之后应该设置一个指向 NULL 的指针.

There seem to be two arguments why one should set a pointer to NULL after freeing them.

简短:第二次调用 free(),当它设置为 NULL 时,意外地不会崩溃.

Short: Calling free() a second time, by accident, doesn't crash when it's set to NULL.

  • 这几乎总是掩盖了一个逻辑错误,因为没有理由第二次调用 free().让应用程序崩溃并能够修复它更安全.

  • Almost always this masks a logical bug because there is no reason to call free() a second time. It's safer to let the application crash and be able to fix it.

不保证会崩溃,因为有时会在同一地址分配新内存.

It's not guaranteed to crash because sometimes new memory is allocated at the same address.

双空闲主要发生在有两个指针指向同一个地址时.

Double free occurs mostly when there are two pointers pointing to the same address.

逻辑错误也会导致数据损坏.

Logical errors can lead to data corruption too.

简短:如果 malloc() 在同一位置分配内存,访问释放的指针会导致数据损坏,除非释放的指针设置为 NULL

Short: Accessing freed pointers can cause data corruption if malloc() allocates memory in the same spot unless the freed pointer is set to NULL

  • 如果偏移量足够大(someStruct->lastMember, theArray[someBigNumber]).不是崩溃,而是数据损坏.

  • There's no guarantee that the program crashes when accessing the NULL pointer, if the offset is big enough (someStruct->lastMember, theArray[someBigNumber]). Instead of crashing there will be data corruption.

将指针设置为NULL并不能解决具有相同指针值的不同指针的问题.

Setting the pointer to NULL cannot solve the problem of having a different pointer with the same pointer value.

这是一篇帖子反对在释放后盲目地设置一个指向NULL的指针.

Here's a post against blindly setting a pointer to NULL after freeing.

  • 哪个更难调试?
  • 是否有可能同时抓住两者?
  • 此类错误导致数据损坏而不是崩溃的可能性有多大?

随意扩展这个问题.

推荐答案

第二个更重要:重新使用释放的指针可能是一个微妙的错误.您的代码继续正常工作,然后无缘无故地崩溃,因为一些看似无关的代码写入了内存中,而重用的指针恰好指向这些代码.

The second one is way more important: re-using a freed pointer can be a subtle error. Your code keeps right on working, and then crashes for no clear reason because some seemingly unrelated code wrote in the memory that the re-used pointer happens to be pointing at.

我曾经不得不为别人编写的一个非常错误的程序工作.我的直觉告诉我,许多错误与在释放内存后继续使用指针的草率尝试有关.我修改了代码,在释放内存后将指针设置为 NULL,bam,空指针异常开始出现.在我修复了所有空指针异常之后,代码突然稳定多了.

I once had to work on a really buggy program someone else wrote. My instincts told me that many of the bugs were related to sloppy attempts to keep using pointers after freeing the memory; I modified the code to set the pointers to NULL after freeing the memory, and bam, the null pointer exceptions started coming. After I fixed all the null pointer exceptions, suddenly the code was much more stable.

在我自己的代码中,我只调用我自己的函数,它是 free() 的包装器.它需要一个指向指针的指针,并在释放内存后将该指针归零.并且在它调用 free 之前,它调用 Assert(p != NULL); 所以它仍然会捕获对同一指针进行双重释放的尝试.

In my own code, I only call my own function that is a wrapper around free(). It takes a pointer-to-a-pointer, and nulls the pointer after freeing the memory. And before it calls free, it calls Assert(p != NULL); so it still catches attempts to double-free the same pointer.

我的代码也做其他事情,例如(仅在 DEBUG 构建中)在分配内存后立即用一个明显的值填充内存,在调用 free() 之前做同样的事情,以防万一指针的副本等 详情请看这里.

My code does other things too, such as (in the DEBUG build only) filling memory with an obvious value immediately after allocating it, doing the same right before calling free() in case there is a copy of the pointer, etc. Details here.

根据请求,这里是示例代码.

per a request, here is example code.

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() 释放空指针时,没有什么不好的事情发生,因为它会检查指针,如果它已经为空则返回.

You can see in the second function that I need to check the two resource pointers to see if they are not null, and then call FreeAnything(). This is because of the assert() that will complain about a null pointer. I have that assert in order to detect an attempt to double-free, but I don't think it has actually caught many bugs for me; if you want to leave out the asserts, then you can leave out the check and just always call FreeAnything(). Other than the assert, nothing bad happens when you try to free a null pointer with FreeAnything() because it checks the pointer and just returns if it was already null.

我的实际函数名称更简洁,但我试图为这个例子选择自文档化的名称.另外,在我的实际代码中,我有只调试代码,在调用 free() 之前用值 0xDC 填充缓冲区,这样如果我有一个额外的指针内存(不会被清空的内存)很明显它指向的数据是虚假数据.我有一个宏,DEBUG_ONLY(),它在非调试版本中编译为空;和一个宏 FILL() 对结构执行 sizeof() .这两个同样有效:sizeof(FOO)sizeof(*pfoo).所以这里是 FILL() 宏:

My actual function names are rather more terse, but I tried to pick self-documenting names for this example. Also, in my actual code, I have debug-only code that fills buffers with the value 0xDC before calling free() so that if I have an extra pointer to that same memory (one that doesn't get nulled out) it becomes really obvious that the data it's pointing to is bogus data. I have a macro, DEBUG_ONLY(), which compiles to nothing on a non-debug build; and a macro FILL() that does a sizeof() on a struct. These two work equally well: sizeof(FOO) or sizeof(*pfoo). So here is the FILL() macro:

#define FILL(p, b)
    (memset((p), b, sizeof(*(p)))

以下是在调用之前使用 FILL()0xDC 值放入的示例:

Here's an example of using FILL() to put the 0xDC values in before calling:

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

这篇关于在释放它们之后,真的应该将指针设置为“NULL"吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-16 03:06