释放后将变量设置为 NULL

Setting variable to NULL after free

提问人:Alphaneo 提问时间:6/22/2009 最后编辑:SimsonAlphaneo 更新时间:7/24/2020 访问量:130641

问:

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

void some_func () 
{
    int *nPtr;

    nPtr = malloc (100);

    free (nPtr);
    nPtr = NULL;

    return;
}

我觉得,在像上面显示的代码这样的情况下,设置为没有任何意义。还是我错过了什么?NULL

如果在这种情况下没有意义,我将与“质量团队”一起删除此编码规则。请指教。

C 编码样式 malloc 可用 堆内存

评论

6赞 Mouradif 8/4/2016
在对它做任何事情之前,能够检查它是否总是很有用的。如果你不取消你的免费指针,你会得到但仍然无法使用的指针。ptr == NULLptr != NULL
1赞 Константин Ван 1/4/2017
悬空指针可导致可利用的漏洞,例如释放后使用

答:

6赞 Mitch Wheat 6/22/2009 #1

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

8赞 Jared Oberhaus 6/22/2009 #2

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

我可以看到你的论点:既然紧接着就超出了范围,似乎没有理由将其设置为 。但是,对于成员或指针不会立即超出范围的其他位置,它更有意义。目前尚不清楚该指针是否会被不应使用它的代码再次使用。nPtrnPtr = NULLNULLstruct

该规则很可能没有区分这两种情况,因为自动执行该规则要困难得多,更不用说开发人员遵循它了。在每次空闲后设置指针并没有什么坏处,但它有可能指出大问题。NULL

339赞 Martin v. Löwis 6/22/2009 #3

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

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

若要完成样式,还应先初始化指向 NULL 的指针,然后再为其分配真正的指针值。

评论

3赞 Paul Biggar 9/1/2009
我不明白你为什么要“在为它们分配真正的指针值之前初始化指向 NULL 的指针”?
36赞 Martin v. Löwis 9/1/2009
@Paul:在具体情况下,声明可以改为。现在,我同意这将是多余的,下一行后面跟着一个 malloc。但是,如果声明和第一次初始化之间有代码,则有人可能会开始使用该变量,即使它还没有值。如果 null 初始化,则会出现段错误;如果没有,您可能会再次读取或写入随机内存。同样,如果变量后来只是有条件地初始化,那么如果您记得进行 null 初始化,以后的错误访问应该会立即崩溃。int *nPtr=NULL;
1赞 wilhelmtell 12/9/2010
我个人认为,在任何不平凡的代码库中,取消引用 null 的错误与取消引用您不拥有的地址的错误一样模糊。我个人从不打扰。
13赞 Amit Naidu 9/3/2011
Wilhelm,关键是,使用空指针取消引用,您将获得确定的崩溃和问题的实际位置。错误的访问可能会也可能不会崩溃,并在意想不到的地方以意想不到的方式损坏数据或行为。
5赞 Eric 11/16/2012
实际上,初始化指向 NULL 的指针至少有一个明显的缺点:它可以防止编译器警告您有关未初始化的变量。除非代码的逻辑实际上显式处理指针的该值(即 if (nPtr==NULL) dosomething...),否则最好保持原样。
5赞 Steven Canfield 6/22/2009 #4

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

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

评论

0赞 i_am_jorf 6/22/2009
至少在 Windows 上,调试版本会将内存设置为 0xdddddddd因此,当您使用指向已删除内存的指针时,您会立即知道。所有平台上都应该有类似的机制。
2赞 Constantin 7/11/2009
Jeffamaphone,当您再次使用指针时,已删除的内存块可能已被重新分配并分配给另一个对象。
21赞 razzed 6/22/2009 #5

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

请尝试以下操作:

#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_VERSION允许您在调试代码中分析空闲,但两者在功能上是相同的。

编辑:添加了做...如下所述,谢谢。

评论

3赞 Mark Ransom 6/24/2009
如果在不带括号的 if 语句之后使用它,则宏版本有一个微妙的错误。
0赞 jmucchiello 6/25/2009
(void) 0 是怎么回事?此代码执行: if (x) myfree(&x);否则 do_foo();变为 if (x) { free(*(&x)); *(&x) = null; } void 0;否则 do_foo();else 是一个错误。
0赞 jmucchiello 6/25/2009
该宏是逗号运算符的完美位置:free((p)), *(p) = null。当然,下一个问题是它计算 *(p) 两次。它应该是 { void* _pp = (p);免费(*_pp);*_pp = 空;} 预处理器不是很有趣吗?
6赞 Chris Lutz 12/10/2009
宏不应该放在裸括号中,它应该放在块中,这样就不会中断。do { } while(0)if(x) myfree(x); else dostuff();
3赞 Mike Clark 11/13/2010
正如 Lutz 所说,宏观主体是 IMO 使宏观主体“感觉像和工作方式”功能的最佳方式。无论如何,大多数编译器都会优化循环。do {X} while (0)
7赞 Tadeusz A. Kadłubowski 6/22/2009 #6

如果到达已 free()d 的指针,它可能会中断或不中断。该内存可能会被重新分配给程序的另一部分,然后内存损坏,

如果将指针设置为 NULL,则在访问它时,程序始终会崩溃并出现段错误。不再,,有时它起作用'',不再,,以不可预测的方式崩溃''。调试起来更容易。

评论

5赞 Steve Jessop 8/17/2009
程序并不总是因段错误而崩溃。如果访问指针的方式意味着在取消引用之前对其应用了足够大的偏移量,则它可能会到达可寻址内存:((MyHugeStruct *)0)->fieldNearTheEnd。这甚至在您处理完全不会在 0 访问上出现段错误的硬件之前。但是,程序更有可能因段错误而崩溃。
3赞 i_am_jorf 6/22/2009 #7

当您尝试避免以下情况时,此规则很有用:

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

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

在您的场景中,它没有多大意义,但如果函数变长,它可能很重要。

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

一般来说,我建议你在认为这是一个好主意时将其设置为 NULL,而当你认为它不值得时,不要打扰。而是专注于编写简短的函数和精心设计的类。

2赞 Aamir 6/22/2009 #8

为了补充其他人所说的话,指针使用的一个好方法是始终检查它是否是有效的指针。像这样:


if(ptr)
   ptr->CallSomeMethod();

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

评论

5赞 Erich Kitzmueller 6/22/2009
在许多情况下,如果 NULL 指针没有意义,则最好改为编写断言。
3赞 sth 6/22/2009 #9

这可能更像是初始化所有指向 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

7赞 RageZ 12/10/2009 #10

C 语言中最常见的 bug 是 double free。基本上你做这样的事情

free(foobar);
/* lot of code */
free(foobar);

结果非常糟糕,操作系统试图释放一些已经释放的内存,通常它会出现段错误。所以好的做法是设置为 ,这样你就可以进行测试并检查你是否真的需要释放这个内存NULL

if(foobar != null){
  free(foobar);
}

另外要注意的是,它不会做任何事情,所以你不必写 if 语句。我不是真正的操作系统大师,但即使现在大多数操作系统都会在双重释放时崩溃。free(NULL)

这也是为什么所有具有垃圾回收功能的语言(Java、dotnet)都为没有这个问题而感到自豪,也不必将内存管理作为一个整体留给开发人员的主要原因。

评论

12赞 Amber 12/10/2009
实际上,您可以不检查 free() - free(NULL) 被定义为什么都不做。
6赞 Georg Schölly 12/10/2009
这难道不是隐藏了错误吗?(就像释放太多一样。
1赞 StevenWang 12/10/2009
谢谢,我明白了。我试过了:p = (char *)malloc(.....); free(p); if(p!=null) //p!=null is true, p is not null although freed { free(p); //Note: checking doesnot prevent error here }
6赞 Chris Lutz 12/10/2009
正如我所说,不能更改它传递的指针的值。它可以更改指针的内容存储在该地址的数据,但不能更改地址本身指针的值。这将需要(这显然是标准不允许的)或宏(这是允许的并且完全可移植,但人们不喜欢宏)。此外,C 语言不是为了方便,而是为了给程序员尽可能多的控制权。如果他们不希望设置指针的额外开销,则不应将其强加给他们。free(void *ptr)free(void **ptr)NULL
2赞 AnT stands with Russia 12/11/2009
世界上很少有事情可以暴露 C 代码作者缺乏专业精神。但它们包括“在调用之前检查指针是否为 NULL”(以及诸如“转换内存分配函数的结果”或“不加思索地使用带有”的类型名称等内容)。freesizeof
3赞 pierrotlefou 12/10/2009 #11

将刚刚释放的指针设置为 NULL 不是强制性的,而是一种很好的做法。这样,您可以避免 1) 使用释放的尖头 2) 释放它 towice

2赞 sharptooth 12/10/2009 #12

设置 指向 NULL 的指针是为了保护所谓的 double-free - 这种情况是,对于同一地址多次调用 free(),而不在该地址重新分配块。

双重释放会导致未定义的行为 - 通常是堆损坏或立即使程序崩溃。为 NULL 指针调用 free() 不会执行任何操作,因此可以保证安全。

因此,除非您现在确定指针在 free() 之后立即或很快离开范围,否则最佳做法是将该指针设置为 NULL,这样即使再次调用 free(),现在也会调用 NULL 指针并规避未定义的行为。

5赞 Vijay Mathew 12/10/2009 #13

根据 ANSI C 标准:

void free(void *ptr);

free 函数导致空格 PTR 指向要解除分配的, 也就是说,可用于进一步 分配。如果 ptr 为 null 指针, 不执行任何操作。否则,如果 参数与指针不匹配 早些时候由 calloc 返回, malloc 或 realloc 函数,或者如果 空间已由 调用 free 或 realloc ,行为 未定义。

“未定义的行为”几乎总是程序崩溃。为了避免这种情况,将指针重置为 NULL 是安全的。free() 本身无法做到这一点,因为它只传递一个指针,而不是指向指针的指针。您还可以编写一个更安全的 free() 版本,将指针 NULL 化:

void safe_free(void** ptr)
{
  free(*ptr);
  *ptr = NULL;
}

评论

0赞 Chris Lutz 12/10/2009
@DrPizza - 错误(在我看来)是导致您的程序无法正常工作的原因。如果隐藏的双释放破坏了您的程序,则这是一个错误。如果它完全按照预期工作,那就不是错误。
0赞 Georg Schölly 12/10/2009
@DrPizza:我刚刚找到了一个论点,为什么应该设置它以避免屏蔽错误。stackoverflow.com/questions/1025589/......似乎在任何一种情况下,都会隐藏一些错误。NULL
1赞 Secure 12/10/2009
请注意,void 指针到指针有其问题:c-faq.com/ptrs/genericpp.html
3赞 Secure 12/10/2009
@Chris,不,最好的方法是代码结构。不要在代码库中随意抛出 mallocs 和 frees,将相关的东西放在一起。分配资源(内存、文件等)的“模块”负责释放资源,并且必须提供一个函数来执行此操作,以保持对指针的关心。对于任何特定资源,您都只有一个位置分配它和一个位置发布它,两者都靠近。
4赞 DrPizza 12/10/2009
@Chris Lutz:Hogwash。如果编写的代码两次释放同一指针,则程序中存在逻辑错误。通过使其不崩溃来掩盖逻辑错误并不意味着程序是正确的:它仍然在做一些荒谬的事情。没有一种情况是写双重自由是合理的。
46赞 Mike McNertney 12/10/2009 #14

大多数响应都集中在防止双重释放上,但将指针设置为 NULL 还有另一个好处。释放指针后,该内存可以通过对 malloc 的另一次调用重新分配。如果你仍然有原始指针,你最终可能会遇到一个错误,你试图在释放后使用指针并损坏其他一些变量,然后你的程序进入未知状态,各种坏事都可能发生(如果你幸运的话会崩溃,如果你不走运,就会发生数据损坏)。如果在释放后将指针设置为 NULL,则以后通过该指针进行读/写的任何尝试都会导致段错误,这通常比随机内存损坏更可取。

出于这两个原因,最好在 free() 之后将指针设置为 NULL。不过,这并不总是必要的。例如,如果指针变量在 free() 之后立即超出范围,则没有太多理由将其设置为 NULL。

评论

1赞 AnT stands with Russia 12/11/2009
+1 这实际上是一个很好的观点。不是关于“双重自由”的推理(这完全是假的),而是这个。我不喜欢 之后指针的机械 NULL-ing,但这实际上是有道理的。free
0赞 David Schwartz 5/19/2012
如果在通过同一指针释放指针后可以访问指针,则更有可能在通过其他指针释放指针指向的对象后访问指针。所以这对你一点帮助都没有——你仍然必须使用一些其他机制来确保你不通过一个指针访问一个对象,而它通过另一个指针释放它。您也可以使用该方法在相同的指针情况下进行保护。
3赞 mozzbozz 6/1/2014
@DavidSchwartz:我不同意你的评论。几周前,当我不得不为一个大学练习写一个堆栈时,我遇到了一个问题,我调查了几个小时。我在某个时候访问了一些已经释放的内存(释放的几行太早了)。有时它会导致非常奇怪的行为。如果我在释放指针后将指针设置为 NULL,就会出现“简单”段错误,并且我会节省几个小时的工作时间。所以这个答案+1!
3赞 David Schwartz 6/3/2014
@katze_sonne 即使是停止的时钟,每天也正确两次。更有可能的是,设置指向 NULL 的指针将隐藏 bug,方法是防止对已释放对象的错误访问在检查 NULL 的代码中出现段错误,然后以静默方式无法检查它应该检查的对象。(也许在特定调试版本中将指针设置为释放后 NULL 可能会有所帮助,或者将它们设置为保证段错误的 NULL 以外的值可能有意义。但是,这种愚蠢碰巧曾经帮助你,这并不是一个对它有利的论据。
1赞 Den-Jason 10/2/2019
我总是将死指针分配给 NULL,因为它们的寻址内存不再有效。我非常喜欢在发布模式下使用设置为 NULL 的替换值的想法,但类似于在调试模式下,因此您可以检测到任何错误的用法。(void*)0xdeadbeef
3赞 3 revs, 3 users 97%Georg Schölly #15

有两个原因:

避免双重释放时崩溃

RageZ 在一个重复的问题中撰写。

c 中最常见的 bug 是 double 自由。基本上你做这样的事情 那

free(foobar);
/* lot of code */
free(foobar);

结果很糟糕,操作系统尝试 释放一些已经释放的内存和 通常,它是段错误。所以好的 练习是要设置到,所以你 可以进行测试并检查您是否真的 需要释放此内存NULL

if(foobar != NULL){
  free(foobar);
}

另外要注意的是,它不会做任何事情,所以你不必这样做 编写 if 语句。我不是 真的是一个操作系统大师,但我甚至很漂亮 现在大多数操作系统都会在双倍时崩溃 自由。free(NULL)

这也是所有 使用垃圾回收的语言 (Java,dotnet)如此自豪 有这个问题,也没有 不得不留给开发人员 内存管理作为一个整体。

避免使用已释放的指针

由 Martin V. Löwis另一个答案中撰写。

将未使用的指针设置为 NULL 是 防守风格,防止 悬空指针错误。如果晃来晃去 指针在释放后被访问, 您可以随机读取或覆盖 记忆。如果访问了 null 指针, 大多数情况下,您都会立即崩溃 系统,马上告诉你什么 错误是。

对于局部变量,它可能是 如果是的话,有点毫无意义 “明显”指针不是 被释放后再访问,所以 这种风格更适合 成员数据和全局变量。甚至 对于局部变量,它可能是一个很好的 如果功能继续,则接近 释放内存后。

要完成样式,您还应该 在之前初始化指向 NULL 的指针 他们被分配了一个真正的指针 价值。

4赞 jcoder 12/10/2009 #16

我发现这没什么帮助,因为根据我的经验,当人们访问释放的内存分配时,几乎总是因为他们在某处有另一个指向它的指针。然后它与另一个个人编码标准冲突,即“避免无用的混乱”,所以我不这样做,因为我认为它很少有帮助,并且使代码的可读性略低。

但是 - 如果指针不应该再次使用,我不会将变量设置为 null,但通常更高级别的设计会让我有理由将其设置为 null。例如,如果指针是某个类的成员,并且我已经删除了它所指向的内容,那么如果您喜欢该类,则该类的“契约”是该成员将随时指向有效内容,因此必须将其设置为 null。这是一个很小的区别,但我认为是一个重要的区别。

在 c++ 中,在分配一些内存时,始终考虑谁拥有这些数据是很重要的(除非你使用的是智能指针,但即使如此,也需要一些思考)。这个过程往往会导致指针通常是某个类的成员,并且通常你希望一个类始终处于有效状态,最简单的方法是将成员变量设置为 NULL,以指示它现在不指向任何内容。

一种常见的模式是在构造函数中将所有成员指针设置为 NULL,并让析构函数对指向设计声明该类拥有的数据的任何指针调用 delete。显然,在这种情况下,您必须在删除某些内容时将指针设置为 NULL,以表明您之前不拥有任何数据。

总而言之,是的,我经常在删除某些内容后将指针设置为 NULL,但这是作为更大的设计和关于谁拥有数据的想法的一部分,而不是由于盲目遵循编码标准规则。在您的示例中,我不会这样做,因为我认为这样做没有任何好处,并且它增加了“混乱”,根据我的经验,这与此类事情一样会导致错误和错误代码。

43赞 AnT stands with Russia 12/10/2009 #17

设置指向 after 的指针是一种可疑的做法,它经常被推广为“好的编程”规则,其前提显然是错误的。它是属于“听起来正确”类别的虚假真相之一,但实际上绝对没有任何用处(有时会导致负面后果)。NULLfree

据称,设置指向 after 的指针应该可以防止在多次传递相同的指针值时出现可怕的“双重释放”问题。但实际上,在 10 种情况下,有 9 种情况是,当使用具有相同指针值的不同指针对象作为 的参数时,就会出现真正的“双重释放”问题。毋庸置疑,在这种情况下,设置指向 after 的指针绝对无法防止出现问题。NULLfreefreefreeNULLfree

当然,当使用相同的指针对象作为参数时,可能会遇到“双重释放”问题。然而,在现实中,这样的情况通常表明代码的一般逻辑结构存在问题,而不仅仅是偶然的“双重释放”。在这种情况下,处理问题的正确方法是审查和重新考虑代码的结构,以避免出现多次传递同一指针的情况。在这种情况下,将指针设置为“已解决”的问题,只不过是试图将问题扫地出门。在一般情况下,它根本行不通,因为代码结构的问题总是会找到另一种方式来表现自己。freefreeNULL

最后,如果您的代码专门设计为依赖于指针值 being or not ,则将指针值设置为 after 是完全可以的。但作为一般的“良好做法”规则(如“始终将指针设置为”),它再次成为众所周知且毫无用处的假货,经常被一些人出于纯粹的宗教、巫毒教般的原因而遵循。NULLNULLNULLfreeNULLfree

评论

1赞 Left For Archive 12/11/2009
绝对。我不记得曾经导致过双重释放,该释放可以通过在释放后将指针设置为 NULL 来修复,但我造成了很多不会。
6赞 Coder 7/25/2015
@AnT“可疑”有点多。这完全取决于用例。如果指针的值曾经在真/假意义上使用,这不仅是一种有效的做法,而且是一种最佳实践。
1赞 David Schwartz 1/19/2017
@Coder 完全错误。如果指针的值在真正的错误意义上用于知道它是否指向调用 free 之前的对象,这不仅不是最佳实践,而且是错误的。例如:。在这里,在调用 to 之后设置为将导致函数认为它从未有过柱线并返回错误的值!foo* bar=getFoo(); /*more_code*/ free(bar); /*more_code*/ return bar != NULL;barNULLfree
3赞 jimhark 9/6/2018
我不认为主要的好处是防止双重释放,而是更早、更可靠地捕捉到悬空指针。例如,在释放包含资源、指向已分配内存的指针、文件句柄等的结构时,当我释放包含的内存指针并关闭包含的文件时,我会为各自的成员清空。然后,如果错误地通过悬空指针访问了其中一个资源,则程序每次都会出错。否则,如果没有 NULLing,释放的数据可能尚未被覆盖,并且 bug 可能不容易重现。
2赞 mfloris 3/26/2019
我同意结构良好的代码不应该允许在释放后访问指针或释放两次指针的情况。但在现实世界中,我的代码将被修改和/或维护,这个人可能不认识我,也没有时间和/或技能来正确地做事(因为截止日期总是昨天)。因此,我倾向于编写即使误用也不会使系统崩溃的防弹函数。
1赞 Jens 4/10/2011 #18

由于您有一个质量保证团队,让我补充一点关于 QA 的小观点。一些针对 C 的自动化 QA 工具会将对释放指针的赋值标记为“无用的赋值”。例如,Gimpel Software 的 PC-lint/FlexeLint 说ptrtst.c 8 Warning 438: Last value assigned to variable 'nPtr' (defined at line 5) not used

有一些方法可以有选择地禁止显示消息,因此,如果您的团队决定这样做,您仍然可以同时满足这两个 QA 要求。

2赞 Jaap Weel 9/7/2011 #19

这个想法是,如果你试图在释放不再有效的指针后取消引用它,你希望失败(段错误),而不是默默地和神秘地失败。

但。。。小心。如果取消引用 NULL,并非所有系统都会导致段错误。在(至少某些版本的)AIX 上,*(int *)0 == 0,并且 Solaris 具有与此 AIX“功能”的可选兼容性。

2赞 Bill IV 10/27/2011 #20

对于最初的问题: 在释放内容后直接将指针设置为 NULL 完全是浪费时间,前提是代码满足所有要求,已完全调试并且永远不会再次修改。另一方面,当有人不经意地在 free() 下添加一个新代码块时,当原始模块的设计不正确时,以及在它编译但不做我想要的错误的情况下,防御性地 NULLing 一个已释放的指针可能非常有用。

在任何系统中,都有一个无法实现的目标,即让事情变得最简单,以及不准确测量的不可减少的成本。在C语言中,我们得到了一套非常锋利、非常坚固的工具,它们可以在熟练工人的手中创造很多东西,如果处理不当,会造成各种隐喻性的伤害。有些很难理解或正确使用。而人们,天生厌恶风险,会做一些非理性的事情,比如在调用 free 之前检查指针是否有 NULL 值......

测量问题是,每当你试图将好与差分开时,情况越复杂,你就越有可能得到一个模棱两可的测量。如果目标是只保持良好的做法,那么一些模棱两可的做法就会被扔掉,实际上并不好。如果你的目标是消除不好的东西,那么模棱两可的东西可能会留在好的地方。这两个目标,只保持好或消除明显的坏,似乎是截然相反的,但通常有第三组既不是其中之一,也不是另一个,两者兼而有之。

在向质量部门提出案例之前,请尝试查看错误数据库,以了解无效指针值导致必须写下的问题的频率(如果有的话)。如果你想做出真正的改变,请确定生产代码中最常见的问题,并提出三种方法来防止它

评论

0赞 jimhark 9/6/2018
好答案。我想补充一件事。出于各种原因,查看错误数据库是件好事。但是在原始问题的上下文中,请记住,很难知道有多少无效指针问题被阻止,或者至少很早就被发现,以至于没有进入错误数据库。Bug 历史记录为添加编码规则提供了更好的证据。
6赞 Jalindar 10/9/2012 #21

最近,我在寻找答案后遇到了同样的问题。我得出了这个结论:

这是最佳实践,必须遵循此原则才能使其在所有(嵌入式)系统上具有可移植性。

free()是一个库函数,它随着平台的变化而变化,因此不应期望在将指针传递给此函数并释放内存后,此指针将设置为 NULL。对于为平台实现的某些库,情况可能并非如此。

所以总是去

free(ptr);
ptr = NULL;
4赞 pkthapa 9/3/2016 #22

始终建议声明一个带有 NULL 的指针变量,例如,

int *ptr = NULL;

假设 ptr 指向0x1000内存地址。 使用 后,始终建议通过再次声明为 NULL 来使指针变量无效。 例如:free(ptr)

free(ptr);
ptr = NULL;

如果未重新声明为 NULL,则指针变量仍会继续指向同一地址 (0x1000),此指针变量称为悬空指针。 如果定义另一个指针变量(比如 q)并动态地将地址分配给新指针,则有可能通过新的指针变量获取相同的地址 (0x1000)。如果您使用相同的指针 (ptr) 并更新同一指针 (ptr) 指向的地址处的值,则程序最终会将值写入 q 指向的位置(因为 pq 指向相同的地址 (0x1000)。

例如

*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.
1赞 Ehsan 2/24/2017 #23

长话短说:您不想意外(错误地)访问已释放的地址。因为,当您释放地址时,您允许将堆中的该地址分配给其他应用程序。

但是,如果未将指针设置为 NULL,并且错误地尝试取消引用指针,或更改该地址的值;你仍然可以做到。但不是你逻辑上想要做的事情。

为什么我仍然可以访问已释放的内存位置?因为:您可能已经释放了内存,但指针变量仍然包含有关堆内存地址的信息。因此,作为防御策略,请将其设置为 NULL。