空闲后将variables设置为NULL
在我的公司有一个编码规则,说释放任何内存后,将该variables重置为NULL。 例如 …
void some_func () { int *nPtr; nPtr = malloc (100); free (nPtr); nPtr = NULL; return; }
我觉得,在像上面显示的代码的情况下,设置为NULL没有任何意义。 还是我错过了什么?
如果在这种情况下没有意义的话,我会去接受“质量小组”去除这个编码规则。 请指教。
将未使用的指针设置为NULL是一种防御风格,可以防止悬挂的指针错误。 如果悬空指针在释放后被访问,则可以读取或覆盖随机存储器。 如果访问一个空指针,你会立即在大多数系统上崩溃,马上告诉你错误是什么。
对于局部variables,如果指针在被释放后不再被访问是“显而易见的”,那么可能有点没有意义,所以这种样式更适合于成员数据和全局variables。 即使对于局部variables,如果内存释放后function仍然存在,这可能是一个好方法。
为了完成这个样式,你还应该在指定一个真指针值之前初始化指向NULL的指针。
free
之后将指针设置为NULL
是一个可疑的练习,在一个明显错误的前提下经常被推广为“良好的编程”规则。 这是属于“听起来正确”类别的虚假真理之一,但在现实中实现绝对没有用(有时导致消极后果)。
据说,在free
后设置一个NULL
指针可以防止当同一个指针值被多次free
时释放可怕的“双免费”问题。 但实际上,在10个中有9个是真正的“双重自由”问题,当不同的指针对象持有相同的指针值被用作free
参数时。 不用说,在free
情况下将指针设置为NULL
,在这种情况下完全没有办法阻止这个问题。
当然,使用相同的指针对象作为参数free
时,可能遇到“双重释放”的问题。 但是,在现实情况下,这样的情况通常表明代码的一般逻辑结构存在问题,而不是偶然的“双重自由”。 在这种情况下处理这个问题的一个正确的方法就是检查和重新思考代码的结构,以避免同一个指针被多次free
的情况。 在这种情况下,将指针设置为NULL
并考虑到“固定”的问题只不过是试图扫描地毯下的问题。 它在一般情况下不起作用,因为代码结构的问题总是会find另一种performance自己的方式。
最后,如果您的代码是专门devise为依赖于指针值为NULL
或不为NULL
,那么free
后将指针值设置为NULL
是完全正确的。 但是作为一个普遍的“良好实践”规则(就像在“总是把你的指针设置成free
后的NULL
指针”一样),它又是一个众所周知的,相当无用的假冒,往往是纯粹的宗教巫术。
大部分响应集中在阻止双重释放,但将指针设置为NULL还有另一个好处。 一旦释放了一个指针,就可以通过对malloc的另一个调用来重新分配该内存。 如果你仍然有原始的指针,你可能会得到一个错误,你试图使用指针后,免费损坏了一些其他的variables,然后你的程序进入一个未知的状态,各种不好的事情可能发生(如果你幸运的是,如果你不幸的话,数据损坏)。 如果您在释放后将指针设置为NULL,则稍后尝试通过该指针进行读/写操作将导致段错误,这通常比随机内存损坏更可取。
由于这两个原因,在free()之后将指针设置为NULL可能是一个好主意。 但是,这并不总是必要的。 例如,如果指针variables在free()之后立即超出作用域,则没有太多理由将其设置为NULL。
这被认为是避免覆盖内存的良好做法。 在上面的函数中,这是不必要的,但是通常当它完成时可以发现应用程序错误。
尝试这样的事情,而不是:
#if DEBUG_VERSION void myfree(void **ptr) { free(*ptr); *ptr = null; } #else #define myfree(p) do { void ** __p = (p); free(*(__p)); *(__p) = null; } while (0) #endif
DEBUG_VERSION允许您在debugging代码中分析释放,但两者在function上是相同的。
编辑 :添加做…如下build议,谢谢。
如果到达空闲指针()d,它可能会中断或不中断。 该内存可能会重新分配给您的程序的另一部分,然后你得到内存损坏,
如果您将指针设置为NULL,那么如果您访问它,程序总是崩溃与段错误。 没有更多的,有时候是有效的“,不再以不可预测的方式崩溃”。 debugging起来很方便。
将指针设置为free
内存意味着任何通过指针访问内存的尝试都会立即崩溃,而不会导致未定义的行为。 这使得更容易确定事情出错的地方。
我可以看到你的参数:因为nPtr
在nPtr = NULL
之后nPtr = NULL
超出了范围,似乎没有理由将其设置为NULL
。 但是,对于struct
成员或其他地方的指针不会立即超出范围的情况,这更有意义。 不知道该指针是否会被不应该使用的代码再次使用。
这个规则很可能没有区分这两种情况,因为自动执行规则要困难得多,更不用说开发人员要遵循这个规则了。 每一次免费后都不要指向NULL
,但它有可能指出很大的问题。
c中最常见的错误是double free。 基本上你做这样的事情
free(foobar); /* lot of code */ free(foobar);
而且最终结果很糟糕,操作系统试图释放一些已经释放的内存,通常是段错误。 所以最好的做法是设置为NULL
,所以你可以testing和检查是否真的需要释放这个内存
if(foobar != null){ free(foobar); }
还要注意的是, free(NULL)
不会做任何事情,所以你不必写if语句。 我不是一个真正的OS大师,但我很漂亮,即使现在大多数操作系统会崩溃双免费。
这也是所有具有垃圾回收function的语言(Java,dotnet)为什么没有这个问题而感到自豪的原因,也没有把开发者的内存pipe理作为一个整体。
这背后的想法是停止意外重用释放的指针。
这(可)实际上是重要的。 虽然你释放了记忆,但是程序的后面部分可以分配一些新的东西在空间中。 您的旧指针现在将指向一个有效的内存块。 那么有人可能会使用指针,导致无效的程序状态。
如果你的指针是空的,那么任何使用它的尝试都会解引用0x0并在那里崩溃,这很容易debugging。 指向随机存储器的随机指针很难debugging。 这显然没有必要,但这就是为什么它在最佳实践文档。
从ANSI C标准:
void free(void *ptr);
自由函数使ptr指向的空间被释放,也就是说,可用于进一步的分配。 如果ptr是空指针,则不会发生任何操作。 否则,如果参数与先前由calloc,malloc或realloc函数返回的指针不匹配,或者如果通过调用free或realloc释放空间,则行为是未定义的。
“未定义的行为”几乎总是程序崩溃。 为了避免这种情况,将指针重置为NULL是安全的。 free()本身不能做到这一点,因为它只传递一个指针,而不是一个指向指针的指针。 您也可以编写一个更安全的free()函数来使指针NULL:
void safe_free(void** ptr) { free(*ptr); *ptr = NULL; }
我觉得这是没有什么帮助的,因为在我的经验中,当人们访问一个释放的内存分配时,几乎总是因为他们有另一个指向它的地方。 然后它与另一个个人编码标准“避免无用的混乱”相冲突,所以我不这样做,因为我觉得它很less有帮助,并且使得代码的可读性稍差。
但是 – 如果指针不应该被再次使用,我不会将variables设置为null,但是更高层次的devise经常会让我有理由将其设置为null。 例如,如果指针是一个类的成员,我已经删除了它指向的内容,那么如果你喜欢这个类,那么“合约”就是那个成员将随时指向某个有效的东西,所以它必须被设置为null是因为。 一个小小的区别,但我认为重要的一个。
在c ++中,当你分配一些内存的时候,总是想着谁拥有这些数据是非常重要的(除非你使用智能指针,但是即使需要一些思路)。 而这个过程往往会导致指针通常是某个类的成员,并且通常希望某个类始终处于有效状态,最简单的方法是将成员variables设置为NULL以指示它的点现在什么也没有
一个常见的模式是在构造函数中将所有成员指针设置为NULL,并且让析构函数调用指向该devise表示该类拥有的数据的指针。 很明显,在这种情况下,当你删除一些东西以表明你之前没有任何数据时,你必须将指针设置为NULL。
所以总结一下,是的,我经常在删除某些东西后将指针设置为NULL,但是这是作为一个更大的devise和谁拥有数据的想法的一部分,而不是盲目地遵循编码标准规则。 我不会这样做,因为我认为这样做没有任何好处,它增加了“混乱”,根据我的经验,这是对错误和错误代码的负责。
最近我在查找答案后遇到同样的问题。 我得出这个结论:
这是最佳实践,必须遵循这一点才能在所有(embedded式)系统上进行移植。
free()
是一个库函数,它随着平台的变化而变化,所以你不应该期望在传递指针指向这个函数之后,释放内存之后,这个指针将被设置为NULL。 某些为该平台实施的图书馆可能并非如此。
所以总是去
free(ptr); ptr = NULL;
当您尝试避免以下情况时,此规则很有用:
1)你有一个很长的函数,具有复杂的逻辑和内存pipe理,你不希望在函数后面意外地重复使用指针来删除内存。
2)指针是一个具有相当复杂的行为的类的成员variables,你不想意外地重复使用指针删除其他函数中的内存。
在你的情况下,这并没有太多的意义,但如果function变得更长,这可能很重要。
你可能会认为把它设置为NULL可能会在后面实际上掩盖逻辑错误,或者在你认为它是有效的情况下,你仍然在NULL上崩溃,所以没关系。
一般来说,当你认为这是一个好主意时,我会build议你把它设置为NULL,当你觉得它不值得时,不用担心。 专注于写短小的function和精心devise的课程。
要添加到其他人所说的,指针使用的一个好方法是始终检查它是否是有效的指针。 就像是:
if(ptr) ptr->CallSomeMethod();
if(ptr) ptr->CallSomeMethod();
释放它之后,将指针明确地标记为NULL允许在C / C ++中使用这种types。
这可能是更多的参数来初始化所有指针为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
在堆栈的同一个地方nPtr
,所以它可能仍然包含一个看起来有效的指针。 分配给*p
可能会覆盖各种不相关的东西,并导致丑陋的错误。 特别是如果编译器在debugging模式下用零初始化本地variables,但是一旦优化未打开。 所以debugging版本没有显示任何错误的迹象,而发布版本随机炸毁…
将刚被释放的指针设置为NULL不是强制性的,而是一个好习惯。 这样,你可以避免1)使用一个自由的尖2)释放它
设置指向NULL的指针是为了保护所谓的双重释放(free-free) – 当同一个地址被多次调用free()而不重新分配该地址块时的情况。
双免费导致未定义的行为 – 通常堆腐败或立即崩溃的程序。 为空指针调用free()将不会执行任何操作,因此保证是安全的。
所以最好的做法,除非你现在确定指针离开范围立即或free()后立即将该指针设置为NULL,所以即使再次调用free()它现在被称为一个NULL指针和未定义的行为被回避。
这个想法是,如果你在释放它之后试图解除引用无效指针,那么你想要很难(segfault)而不是默默地和神秘地失败。
不过要小心。 如果您取消引用NULL,则并非所有系统都会导致段错误。 在AIX(至less某些版本)上,*(int *)0 == 0,并且Solaris与此AIX“function”具有可选的兼容性。
有两个原因:
避免双击时崩溃
由RageZ写在一个重复的问题 。
c中最常见的错误是双重释放。 基本上你做这样的事情
free(foobar); /* lot of code */ free(foobar);
而且最终结果很糟糕,操作系统试图释放一些已经释放的内存,并且通常是段错误。 所以最好的做法是设置为
NULL
,所以你可以testing和检查是否真的需要释放这个内存if(foobar != null){ free(foobar); }
还要注意的是,
free(NULL)
不会做任何事情,所以你不必写if语句。 我不是一个真正的OS大师,但我很漂亮,即使现在大多数操作系统会崩溃双免费。这也是所有具有垃圾回收function的语言(Java,dotnet)为什么没有这个问题感到自豪,也没有必要离开开发者整个内存pipe理的主要原因。
避免使用已经释放的指针
马丁诉路易斯在另一个答案写的 。
将未使用的指针设置为NULL是一种防御风格,可以防止悬挂的指针错误。 如果悬空指针在释放后被访问,则可以读取或覆盖随机存储器。 如果访问一个空指针,你会立即在大多数系统上崩溃,马上告诉你错误是什么。
对于局部variables,如果指针在被释放后不再被访问是“显而易见的”,那么可能有点没有意义,所以这种样式更适合于成员数据和全局variables。 即使对于局部variables,如果内存释放后function仍然存在,这可能是一个好方法。
为了完成这个样式,你还应该在指定一个真指针值之前初始化指向NULL的指针。
由于您有一个质量保证小组,所以让我添加一个关于质量保证的小问题。 一些针对C的自动化QA工具会将释放指针的赋值标记为“无用的赋值给ptr
”。 例如,来自Gimpel Software的PC-lint / FlexeLint说tst.c 8 Warning 438: Last value assigned to variable 'nPtr' (defined at line 5) not used
有办法select性地抑制消息,所以如果你的团队决定如此,你仍然可以同时满足质量保证要求。
对于原来的问题:释放内容后直接将指针设置为NULL是一个完全浪费时间,只要代码满足所有要求,完全debugging,不会再被修改。 另一方面,如果有人在free()下面不小心地添加了一个新的代码块,那么在原始模块的devise不正确的情况下,对已释放的指针进行defensively NULL是非常有用的,在这种情况下编译但是并不想做我想要的错误。
在任何系统中,都有一个难以实现的目标,就是使得测量结果最简单,测量结果不准确。 在C语言中,我们提供了一套非常强大,非常强大的工具,它可以在熟练工人的手中创造出许多东西,当处理不当时会造成各种隐喻伤害。 有些难以理解或正确使用。 而且,人们自然而然地冒险,做一些非理性的事情,例如在免费调用之前检查指针是否为空值。
衡量问题是,只要你试图从不好的事物中分一杯羹,事情就越复杂,你就越有可能得到一个模棱两可的测量结果。 如果目标是保持好的做法,那么一些模棱两可的东西就会被抛弃,而实际上并不好。 如果你的目标是消除不好,那么含糊不清的东西可能会留下好的东西。 这两个目标,只保持好或消除明显不好,似乎是截然相反的,但通常有第三组既不是一个,也不是另一个,其中一些。
在您向质量部门提出质疑之前,请尝试查看错误数据库,以查看指针值无效的频率是否会导致需要logging的问题。 如果你想真正改变,找出你的生产代码中最常见的问题,并提出三种防止它的方法
总是build议用NULL来声明一个指针variables,比如,
int *ptr = NULL;
比方说, ptr指向0x1000的内存地址。 使用free(ptr)
,通过再次声明为NULL来清除指针variables总是可取的。 例如:
free(ptr); ptr = NULL;
如果不重新声明为NULL ,则指针variables仍然指向相同的地址( 0x1000 ),这个指针variables被称为悬挂指针 。 如果你定义了另一个指针variables(比方说, q ),并dynamic分配地址给新的指针,那么就有可能通过新的指针variables获取相同的地址( 0x1000 )。 如果你使用相同的指针( ptr )并更新同一个指针( ptr )指向的地址的值,那么程序最终将写入一个值到q指向的地方(因为p和q是指向相同的地址( 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.
长话短说:你不想意外(错误地)访问你已经释放的地址。 因为当你释放地址的时候,你可以把堆中的地址分配给其他的应用程序。
但是,如果不将该指针设置为NULL,并且错误地尝试去引用指针,或者更改该地址的值; 你仍然可以做。 但不是你想要做的事情。
为什么我仍然可以访问我已经释放的内存位置? 因为:您可能已经释放了内存,但指针variables仍然有关于堆内存地址的信息。 所以,作为防御策略,请将其设置为NULL。