在我公司中,有一条编码规则说,释放所有内存后,将变量重置为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
没有任何意义。还是我错过了什么?
如果在这种情况下没有意义,我将与“质量团队”一起解决,以删除此编码规则。请指教。
Answers:
将未使用的指针设置为NULL是一种防御风格,可以防止悬空的指针错误。如果释放了悬空指针,则可以读取或覆盖随机存储器。如果访问了空指针,则在大多数系统上会立即崩溃,并立即告诉您错误是什么。
对于局部变量,如果“很明显”在释放指针后不再访问该指针,可能会毫无意义,因此这种样式更适合于成员数据和全局变量。即使对于局部变量,如果释放内存后函数继续运行,这也是一种好方法。
为了完成样式,您还应该在将指针分配给真指针值之前将其初始化为NULL。
int *nPtr=NULL;
。现在,我同意这将是多余的,在下一行紧跟着一个malloc。但是,如果在声明和第一次初始化之间存在代码,则即使该变量还没有值,也可能有人开始使用该变量。如果您对null进行初始化,则会出现段错误;如果没有,您可能会再次读取或写入随机存储器。同样,如果稍后仅对变量进行有条件的初始化,则如果您记得进行空初始化,则以后的错误访问将使您立即崩溃。
设置指向NULL
之后的指针free
是一种可疑的做法,通常会在明显错误的前提下被普及为“良好编程”规则。这是属于“正确的声音”类别的那些虚假的真理之一,但实际上绝对没有任何用处(有时会导致负面后果)。
据称,将指针设置为NULL
after free
可以防止在相同的指针值被free
多次传递时避免可怕的“双重释放”问题。但是,实际上,在十分之九的情况下,当使用具有相同指针值的不同指针对象作为的参数时,会发生真正的“双重释放”问题free
。不用说,将指针设置为NULL
after free
绝对不能防止这种情况下的问题。
当然,当使用相同的指针对象作为to的参数时,可能会遇到“双重释放”问题free
。但是,在现实情况中,类似的情况通常表示代码的一般逻辑结构存在问题,而不仅仅是偶然的“双重释放”。在这种情况下,解决此问题的一种正确方法是查看并重新考虑代码的结构,以避免同一指针free
多次传递给同一指针的情况。在这种情况下,设置指向NULL
并考虑“已解决”问题的指针无非是试图将问题扫到地毯下。在一般情况下,它根本行不通,因为代码结构问题总会找到另一种方式来表现出来。
最后,如果你的代码是专为依靠指针值存在NULL
与否NULL
,这是完全正常的指针值设置为NULL
后free
。但是,作为一般的“良好做法”规则(例如,“总是将指针指向” NULL
之后free
),它再次是众所周知的且毫无用处的伪造品,通常出于纯粹的宗教性,类似伏都教义的原因而经常出现。
foo* bar=getFoo(); /*more_code*/ free(bar); /*more_code*/ return bar != NULL;
。在这里,在调用bar
to NULL
之后设置为,free
将导致该函数认为它从来没有杠,并返回错误的值!
大多数响应都集中于防止双重释放,但是将指针设置为NULL还有另一个好处。释放指针后,该内存可通过再次调用malloc进行重新分配。如果仍然有原始指针,则可能会遇到一个错误,即在释放并破坏其他变量后尝试使用该指针,然后程序进入未知状态,并且可能发生各种不良情况(如果您很幸运,如果不幸的话,数据损坏。如果在释放后将指针设置为NULL,则以后再尝试通过该指针进行读/写的任何尝试都会导致段错误,通常比随机存储器损坏更可取。
出于两个原因,在free()之后将指针设置为NULL是一个好主意。不过,这并不总是必要的。例如,如果指针变量在free()之后立即超出范围,则没有太多理由将其设置为NULL。
free
,但这确实是有道理的。
这是避免覆盖内存的良好做法。在上面的函数中,这是不必要的,但通常在完成后它会发现应用程序错误。
尝试这样的事情:
#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使您可以在调试代码中分析释放,但是两者在功能上是相同的。
编辑:添加做...,如下所示,谢谢。
do { } while(0)
块中,这样才if(x) myfree(x); else dostuff();
不会损坏。
do {X} while (0)
是IMO制作“感觉像和工作似”功能的最佳方法。无论如何,大多数编译器都会优化循环。
如果到达的指针已经被free()d破坏,则可能会中断或不会中断。该内存可能会重新分配到程序的另一部分,然后您会遇到内存损坏的情况,
如果将指针设置为NULL,则如果访问它,则程序始终会因段错误而崩溃。有时它不再起作用,``不再以不可预测的方式崩溃''。它更容易调试。
将指针设置为free
'd内存意味着通过该指针访问该内存的任何尝试都将立即崩溃,而不是引起未定义的行为。这样可以更轻松地确定问题出在哪里。
我可以看到您的论点:由于此后nPtr
即将超出范围nPtr = NULL
,因此似乎没有理由将其设置为NULL
。但是,对于struct
成员或指针未立即超出范围的其他地方,这更有意义。现在还不应该由不应该使用该指针的代码再次使用该指针。
可能在不区分这两种情况的情况下陈述了该规则,因为自动执行该规则要困难得多,更不用说让开发人员遵循它了。NULL
每次空闲后都将指针设置为无害,但是它有可能指出重大问题。
c语言中最常见的错误是double free。基本上你做这样的事情
free(foobar);
/* lot of code */
free(foobar);
最终结果很糟糕,操作系统尝试释放一些已经释放的内存,并且通常会出现段错误。因此,好的做法是将设置为NULL
,以便您可以进行测试并检查是否确实需要释放此内存
if(foobar != null){
free(foobar);
}
还需要注意的是,它free(NULL)
不会做任何事情,因此您不必编写if语句。我并不是一个真正的操作系统专家,但是即使现在大多数操作系统都会以两次免费使用崩溃。
这也是所有具有垃圾回收功能的语言(Java,dotnet)都以没有出现此问题而自豪,也不必将开发人员整体上留给开发人员而感到自豪的主要原因。
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 }
free(void *ptr)
不能更改它传递的指针的值。它可以更改指针的内容,存储在该地址的数据,但不能更改地址本身或指针的值。这将需要free(void **ptr)
(显然是标准所不允许的)或宏(被允许并且完全可移植,但是人们不喜欢宏)。而且,C并不是为了方便,而是要给程序员尽可能多的控制权。如果他们不想将指针设置为的额外开销NULL
,则不应强加于他们。
free
”(以及诸如“广播内存分配函数的结果”或“不考虑使用类型名称与sizeof
”之类的东西)。
这实际上很重要。尽管您释放了内存,但是程序的后续部分可能会分配一些新的东西,而这些东西恰好落在了空间中。您的旧指针现在将指向有效的内存块。这样一来,有人可能会使用该指针,从而导致无效的程序状态。
如果使指针为NULL,则使用该指针的任何尝试都会取消引用0x0并立即崩溃,这很容易调试。指向随机存储器的随机指针很难调试。显然没有必要,但这就是为什么它包含在最佳实践文档中。
根据ANSI C标准:
void free(void *ptr);
free函数使ptr指向的空间被释放,即可以用于进一步分配。如果ptr是空指针,则不执行任何操作。否则,如果参数与calloc,malloc或realloc函数先前返回的指针不匹配,或者如果通过调用free或realloc释放了空间,则该行为是不确定的。
“未定义的行为”几乎总是导致程序崩溃。为了避免这种情况,将指针重置为NULL是安全的。free()本身无法执行此操作,因为仅将其传递给指针,而不传递给指向指针的指针。您还可以编写更安全的free()版本,使指针为NULL:
void safe_free(void** ptr)
{
free(*ptr);
*ptr = NULL;
}
NULL
以避免掩盖错误。stackoverflow.com/questions/1025589 / ...在这两种情况下,似乎都隐藏了一些错误。
我发现这没有什么帮助,因为根据我的经验,当人们访问释放的内存分配时,几乎总是因为他们在某个地方有另一个指向它的指针。然后,它与另一个个人编码标准“避免无用的混乱”相冲突,所以我不这样做,因为我认为它很少有帮助,并且使代码的可读性稍差。
但是-如果不应再次使用指针,则不会将变量设置为null,但通常更高级别的设计使我有理由将其设置为null。例如,如果指针是类的成员,并且我删除了它指向的内容,那么如果您喜欢该类,则“合同”是该成员将随时指向有效的内容,因此必须将其设置为null是因为。区别很小,但我认为很重要。
在c ++中,重要的是在分配内存时始终要考虑谁拥有此数据(除非您使用的是智能指针,但即使那样也需要一些思考)。而且此过程往往导致指针通常是某个类的成员,并且通常您希望某个类始终处于有效状态,而最简单的方法是将成员变量设置为NULL来指示它指向现在什么都没有。
一种常见的模式是在构造函数中将所有成员指针设置为NULL,并在指向您的设计说该类拥有的数据的任何指针上进行析构函数调用删除。显然,在这种情况下,当您删除某些内容以指示您之前不拥有任何数据时,必须将指针设置为NULL。
因此,总而言之,是的,我经常在删除某些内容后将指针设置为NULL,但这是更大设计的一部分,它是关于谁拥有数据的想法,而不是盲目地遵循编码标准规则。在您的示例中,我不会这样做,因为我认为这样做没有任何好处,并且会增加“混乱”,根据我的经验,这种混乱与此类错误和错误代码的产生同样重要。
当您尝试避免以下情况时,此规则很有用:
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
可能会覆盖各种不相关的内容,并导致难看的错误。特别是如果编译器在调试模式下将局部变量初始化为零,但是一旦启用优化就不会这样做。因此,在发布版本随机爆炸时,调试版本不会显示任何错误迹象...
将刚刚释放的指针设置为NULL不是强制性的,但这是一种好习惯。这样,您就可以避免1)使用释放的指针2)完全释放它
对于最初的问题:释放代码后立即将指针设置为NULL完全是浪费时间,只要代码满足所有要求,并且已完全调试并且以后将不再修改。另一方面,当有人无意地在free()下添加新的代码块,原始模块的设计不正确时(对于这种情况),以防御性的方式使已释放的指针归零可能会非常有用。 -编译但不执行我想要的错误。
在任何系统中,都有一个无法实现的目标,即最容易做到正确的事情,以及无法减少的不准确测量成本。在C语言中,我们提供了一组非常敏锐,非常强大的工具,这些工具可以在技术工人的手中制造很多东西,如果使用不当,会造成各种隐喻性伤害。有些很难理解或正确使用。人们自然会厌恶风险,因此会进行一些非理性的操作,例如在免费调用它之前检查指针是否为NULL值……
度量问题是,每当您尝试将商品与不良商品区分开时,情况越复杂,您获得歧义测量的可能性就越大。如果目标是仅保留良好做法,那么一些模棱两可的做法就会被实际的不好做法抛弃。如果您的目标是消除弊端,那么歧义可能会与弊端保持一致。这两个目标,仅保持良好或消除明显的劣势,似乎是截然相反的,但通常存在第三者既不是一个也不是另一个,两者都是。
在与质量部门联系之前,请尝试查看错误数据库,以查看无效的指针值多久(如果有的话)导致必须记录下来的问题。如果要真正发挥作用,请确定生产代码中最常见的问题,并提出三种预防方法
有两个原因:
c语言中最常见的错误是double free。基本上你做这样的事情
free(foobar); /* lot of code */ free(foobar);
最终结果很糟糕,操作系统尝试释放一些已经释放的内存,并且通常会出现段错误。因此,好的做法是将设置为
NULL
,以便您可以进行测试并检查是否确实需要释放此内存if(foobar != NULL){ free(foobar); }
还应注意的是
free(NULL)
不会做任何事情,因此您不必编写if语句。我并不是一个真正的操作系统专家,但是即使现在大多数操作系统都会以两次免费使用崩溃。这也是为什么所有带有垃圾回收的语言(Java,dotnet)都以没有出现此问题而自豪,也不必整体上由内存管理人员来开发而感到自豪。
将未使用的指针设置为NULL是一种防御风格,可以防止悬空的指针错误。如果释放了悬空指针,则可以读取或覆盖随机存储器。如果访问了空指针,则在大多数系统上会立即崩溃,并立即告诉您错误是什么。
对于局部变量,如果“很明显”在释放指针后不再访问该指针,可能会毫无意义,因此这种样式更适合于成员数据和全局变量。即使对于局部变量,如果释放内存后函数继续运行,这也是一种好方法。
为了完成样式,您还应该在将指针分配给真指针值之前将其初始化为NULL。
始终建议使用NULL声明指针变量,例如,
int *ptr = NULL;
假设,ptr指向0x1000内存地址。使用完后free(ptr)
,建议始终通过再次声明为NULL来使指针变量无效。例如:
free(ptr);
ptr = NULL;
如果未将其重新声明为NULL,则指针变量仍保持指向相同地址(0x1000)的状态,该指针变量称为悬空指针。如果定义另一个指针变量(假设q)并为新指针动态分配地址,则新指针变量可能会占用相同的地址(0x1000)。如果以防万一,您使用相同的指针(ptr)并更新了由相同指针(ptr)指向的地址上的值,则程序最终会将值写入q所指向的位置(因为p和0x1000) )。 q为指向相同的地址(
例如
*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.
ptr == NULL
在进行任何操作之前检查是否总是有用的。如果不取消释放指针,则会得到ptr != NULL
但仍然无法使用的指针。