C 释放后将变量设置为NULL

C 释放后将变量设置为NULL,c,coding-style,malloc,free,heap-memory,C,Coding Style,Malloc,Free,Heap Memory,在我的公司有一条编码规则,规定释放内存后,将变量重置为NULL。例如 void some_func () { int *nPtr; nPtr = malloc (100); free (nPtr); nPtr = NULL; return; } 我觉得,在类似上面显示的代码的情况下,设置为NULL没有任何意义。还是我遗漏了什么 如果在这种情况下没有任何意义,我将与“质量团队”讨论删除此编码规则。请给出建议。这背后的想法是停止意外重用释放的指针。将

在我的公司有一条编码规则,规定释放内存后,将变量重置为
NULL
。例如

void some_func () 
{
    int *nPtr;

    nPtr = malloc (100);

    free (nPtr);
    nPtr = NULL;

    return;
}
我觉得,在类似上面显示的代码的情况下,设置为
NULL
没有任何意义。还是我遗漏了什么


如果在这种情况下没有任何意义,我将与“质量团队”讨论删除此编码规则。请给出建议。

这背后的想法是停止意外重用释放的指针。

将指针设置为
空闲
'd内存意味着任何通过指针访问该内存的尝试都将立即崩溃,而不是导致未定义的行为。这使得确定哪里出了问题变得更加容易

我可以看到你的论点:因为
nPtr
nPtr=NULL
之后就超出了范围,所以似乎没有理由将其设置为
NULL
。但是,对于
struct
成员或指针没有立即超出范围的其他地方,它更有意义。不应该使用该指针的代码是否会再次使用该指针,目前还不清楚


很可能规则是在没有区分这两种情况的情况下陈述的,因为自动执行规则要困难得多,更不用说让开发人员遵循规则了。在每次空闲后将指针设置为空并不有害,但它有可能指出大问题。

将未使用的指针设置为空是一种防御方式,可以防止指针出现悬空错误。如果悬空指针在释放后被访问,则可以读取或覆盖随机内存。如果访问空指针,大多数系统会立即崩溃,并立即告诉您错误是什么

对于局部变量,如果“明显”指针在释放后不再被访问,则可能有点毫无意义,因此这种样式更适合于成员数据和全局变量。即使对于局部变量,如果函数在释放内存后继续,也可能是一个好方法

要完成该样式,您还应该在指针被指定一个真正的指针值之前将指针初始化为NULL。

这(可能)实际上很重要。虽然您释放了内存,但程序的后续部分可能会分配一些新的内容,这些内容碰巧会降落在空间中。您的旧指针现在将指向一个有效的内存块。然后可能有人会使用指针,导致程序状态无效


如果将指针设为NULL,那么任何使用它的尝试都将取消引用0x0并在那里崩溃,这很容易调试。指向随机内存的随机指针很难调试。这显然不是必需的,但这就是为什么它会出现在最佳实践文档中。

这被认为是避免覆盖内存的良好实践。在上面的函数中,这是不必要的,但通常在完成后会发现应用程序错误

请尝试以下方法:

#if DEBUG_VERSION
void myfree(void **ptr)
{
    free(*ptr);
    *ptr = NULL;
}
#else
#define myfree(p) do { void ** p_tmp = (p); free(*(p_tmp)); *(p_tmp) = NULL; } while (0)
#endif
DEBUG_版本允许您在调试代码中分析空闲,但两者在功能上是相同的


编辑:添加了do。。。正如下面所建议的,谢谢。

如果您到达已释放的指针()d,它可能会断开或不断开。内存可能会被重新分配到程序的另一部分,然后导致内存损坏


如果将指针设置为NULL,那么如果访问它,程序总是会因segfault而崩溃。不再,有时它工作“”,不再,以不可预测的方式崩溃“”。这更容易调试。

当您试图避免以下情况时,此规则非常有用:

1) 您有一个非常长的函数,具有复杂的逻辑和内存管理,您不希望在函数的后面意外地重用指向已删除内存的指针

2) 指针是具有相当复杂行为的类的成员变量,您不希望意外地在其他函数中重用指向已删除内存的指针

在您的场景中,这没有多大意义,但如果函数变长,可能会有影响

您可能会争辩说,将其设置为NULL实际上可能会在以后掩盖逻辑错误,或者在您假设它有效的情况下,仍然会在NULL上崩溃,所以这无关紧要


一般来说,当你认为这是一个好主意时,我建议你将它设置为NULL,当你认为它不值得时,不要麻烦。相反,要专注于编写简短的函数和设计良好的类。

要补充其他人所说的内容,使用指针的一个好方法是始终检查它是否是有效的指针。比如:


if(ptr)
   ptr->CallSomeMethod();

释放指针后将其显式标记为NULL允许在C/C++中使用这种用法。

这可能更像是一个将所有指针初始化为NULL的参数,但类似的东西可能是一个非常隐蔽的错误:

void other_func() {
  int *p; // forgot to initialize
  // some unrelated mallocs and stuff
  // ...
  if (p) {
    *p = 1; // hm...
  }
}

void caller() {
  some_func();
  other_func();
}

p
与前一个
nPtr
在堆栈上的位置相同,因此它可能仍然包含一个看似有效的指针。分配给
*p
可能会覆盖所有不相关的内容并导致丑陋的bug。特别是如果编译器在调试模式下使用零初始化局部变量,但在启用优化后不进行初始化。因此,当发布版本随机爆炸时,调试版本不会显示任何错误迹象…

c中最常见的错误是双自由。基本上你是这样做的

free(foobar);
/* lot of code */
free(foobar);
结果非常糟糕,操作系统试图释放一些已经释放的内存,通常会出错。因此,最好将设置为
NULL
,这样您就可以进行测试并检查是否确实需要释放此内存

if(foobar != null){
  free(foobar);
}
if(foobar != NULL){
  free(foobar);
}
还要注意的是,
free(NULL)
不会做任何事情,因此您不必编写if语句。我并不是一个真正的操作系统大师,但即使是现在,大多数操作系统都会因双重免费而崩溃

这也是一个主要的问题
void safe_free(void** ptr)
{
  free(*ptr);
  *ptr = NULL;
}
free(foobar);
/* lot of code */
free(foobar);
if(foobar != NULL){
  free(foobar);
}
free(ptr);
ptr = NULL;
int *ptr = NULL;
free(ptr);
ptr = NULL;
*ptr = 20; //Points to 0x1000
free(ptr);
int *q = (int *)malloc(sizeof(int) * 2); //Points to 0x1000
*ptr = 30; //Since ptr and q are pointing to the same address, so the value of the address to which q is pointing would also change.