释放实例的函数struct Foo
如下:
void DestroyFoo(Foo* foo) { if (foo) free(foo); }
我的一位同事建议如下:
void DestroyFoo(Foo** foo) { if (!(*foo)) return; Foo *tmpFoo = *foo; *foo = NULL; // prevents future concurrency problems memset(tmpFoo, 0, sizeof(Foo)); // problems show up immediately if referred to free memory free(tmpFoo); }
我看到NULL
在释放后将指针设置为更好,但我不确定以下内容:
我们真的需要将指针分配给临时指针吗?它在并发性和共享内存方面有帮助吗?
将整个块设置为0以强制程序崩溃或至少输出具有显着差异的结果真的是一个好主意吗?
P.P... 67
我们真的需要将指针分配给临时指针吗?它在并发性和共享内存方面有帮助吗?
它与并发或共享内存无关.这是毫无意义.
将整个块设置为0以强制程序崩溃或至少输出具有显着差异的结果真的是一个好主意吗?
一点都不.
你的同事建议的解决方案很糟糕.原因如下:
将整个块设置为0也没有任何结果.因为有人正在意外地使用free()'ed块,他们根据块的值不知道这个.这就是那种块calloc()
回报.因此,不可能知道它是新分配的内存(calloc()
或malloc()+memset()
)还是之前由您的代码释放的内存().如果有的话,你的程序需要额外的工作来清空每个被释放的内存块().
free(NULL);
是明确的,是一个无操作,所以if
条件if(ptr) {free(ptr);}
没有实现.
既然free(NULL);
是no-op,设置指针NULL
实际上会隐藏那个bug,因为如果某个函数实际调用free()
已经free()'ed指针,那么他们就不会知道.
大多数用户函数在开始时都会进行NULL检查,并且可能不会考虑将NULL
其作为错误条件传递给它:
void do_some_work(void *ptr) { if (!ptr) { return; } /*Do something with ptr here */ }
因此,所有这些额外的检查和归零都给人一种虚假的"健壮性"感,而它并没有真正改善任何东西.它只是将另一个问题替换为性能和代码膨胀的额外成本.
所以只是打电话free(ptr);
没有任何包装的功能是既简单又强大的(大多数malloc()
的实现将立即崩溃双免费的,这是一个很好的事情).
"意外"呼叫free()
两次或更多次是没有简单的方法.程序员有责任跟踪分配的所有内存并free()
适当地跟踪它.如果有人发现这很难处理,那么C可能不适合他们.
我们真的需要将指针分配给临时指针吗?它在并发性和共享内存方面有帮助吗?
它与并发或共享内存无关.这是毫无意义.
将整个块设置为0以强制程序崩溃或至少输出具有显着差异的结果真的是一个好主意吗?
一点都不.
你的同事建议的解决方案很糟糕.原因如下:
将整个块设置为0也没有任何结果.因为有人正在意外地使用free()'ed块,他们根据块的值不知道这个.这就是那种块calloc()
回报.因此,不可能知道它是新分配的内存(calloc()
或malloc()+memset()
)还是之前由您的代码释放的内存().如果有的话,你的程序需要额外的工作来清空每个被释放的内存块().
free(NULL);
是明确的,是一个无操作,所以if
条件if(ptr) {free(ptr);}
没有实现.
既然free(NULL);
是no-op,设置指针NULL
实际上会隐藏那个bug,因为如果某个函数实际调用free()
已经free()'ed指针,那么他们就不会知道.
大多数用户函数在开始时都会进行NULL检查,并且可能不会考虑将NULL
其作为错误条件传递给它:
void do_some_work(void *ptr) { if (!ptr) { return; } /*Do something with ptr here */ }
因此,所有这些额外的检查和归零都给人一种虚假的"健壮性"感,而它并没有真正改善任何东西.它只是将另一个问题替换为性能和代码膨胀的额外成本.
所以只是打电话free(ptr);
没有任何包装的功能是既简单又强大的(大多数malloc()
的实现将立即崩溃双免费的,这是一个很好的事情).
"意外"呼叫free()
两次或更多次是没有简单的方法.程序员有责任跟踪分配的所有内存并free()
适当地跟踪它.如果有人发现这很难处理,那么C可能不适合他们.
如果函数被调用两次,你的同事建议将使代码"更安全"(参见sleske评论......因为"更安全"对每个人来说可能并不一样...... ;-).
使用您的代码,这很可能会崩溃:
Foo* foo = malloc( sizeof(Foo) ); DestroyFoo(foo); DestroyFoo(foo); // will call free on memory already freed
使用您同事的代码版本,这不会崩溃:
Foo* foo = malloc( sizeof(Foo) ); DestroyFoo(&foo); DestroyFoo(&foo); // will have no effect
现在,对于这个特定的场景,做tmpFoo = 0;
(内DestroyFoo
)就足够了.memset(tmpFoo, 0, sizeof(Foo));
如果Foo具有在释放内存后可能被错误访问的额外属性,则会阻止崩溃.
所以我会说是的,这样做可能是一个很好的做法....但它只是一种安全措施,对付有不良做法的开发人员(因为没有理由在DestroyFoo
没有重新分配的情况下调用两次) ...结束,你使DestroyFoo
"更安全"但更慢(它做更多的东西,以防止它的不良使用).