这是一个在C中释放内存的好方法吗?

释放struct Foo实例的函数如下所示:

void DestroyFoo(Foo* foo) { if (foo) free(foo); } 

我的一位同事build议改为:

 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更好,但我不确定以下内容:

  1. 我们是否真的需要将指针分配给临时指针? 它在并发和共享内存方面有帮助吗?
  2. 将整个块设置为0以强制程序崩溃或至less输出有显着差异的结果是一个好主意吗?

提前致谢!

我们是否真的需要将指针分配给临时指针? 它在并发和共享内存方面有帮助吗?

它没有任何并发​​或共享内存。 没有用。

将整个块设置为0以强制程序崩溃或至less输出有显着差异的结果是一个好主意吗?

一点都不。

你的同事提出的解决scheme是可怕的。 原因如下:

  • 将整个块设置为0也不能实现。 因为有人不小心使用了免费()的ed块,所以他们根据块的值不会知道这个值。 这就是calloc()返回的那种块。 因此,不可能知道它是否是刚分配的内存( calloc()malloc()+memset() ),或者是之前由代码自由分配的内存。 如果有的话,你的程序需要额外的工作来清空每个被释放的内存块。

  • free(NULL); 是明确的,是一个无操作,所以if(ptr) {free(ptr);}if条件什么也得不到。

  • 由于free(NULL); 是空操作,将指针设置为NULL实际上会隐藏该错误,因为如果某个函数实际上在已经释放()的ed指针上调用free() ,那么他们就不会知道这一点。

  • 大多数用户函数会在开始时进行NULL检查,可能不会考虑将NULL传递给它作为错误条件:

 void do_some_work(void *ptr) { if (!ptr) { return; } /*Do something with ptr here */ } 

因此,所有这些额外的检查和归零都给人一种“健壮性”的假感,而这并没有真正改善任何事情。 它只是用另一个性能和代码膨胀成本代替了另一个问题。

所以只需拨打free(ptr); 没有任何包装函数是简单和强大的(大多数malloc()实现将崩溃立即双倍免费,这是一件好事)。

free()两次或更多的“ free() ”调用没有简单的方法。 程序员有责任跟踪所有分配的内存,并适当地free() 。 如果有人觉得这很难处理,那么C可能不是他们正确的语言。

你的同事提出的build议将使代码“更安全”,以防函数被调用两次(参见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 “更安全”,但速度更慢(它做了更多的东西,以防止使用不当)。

第二个解决scheme似乎是过度devise的。 当然在某些情况下可能会更安全,但是开销和复杂性太大了。

如果你想保证安全,你应该做的是在释放内存后将指针设置为NULL。 这总是一个很好的做法。

 Foo* foo = malloc( sizeof(Foo) ); DestroyFoo(foo); foo = NULL; 

更重要的是,我不知道为什么人们在调用free()之前检查指针是否为NULL。 这是不需要的,免费()将为你做这项工作。

将内存设置为0(或其他)只是在某些情况下,一个好的做法是free()不会清除内存。 它只会标记一个内存区域,使其可以被重用。 如果你想清除内存,以便没有人能够阅读它,你需要手动清理。 但这是相当繁重的操作,这就是为什么这不应该被用来释放所有的内存。 在大多数情况下,没有清除的情况下释放就够了,你不必牺牲性能来做不必要的操作。

 void destroyFoo(Foo** foo) { if (!(*foo)) return; Foo *tmpFoo = *foo; *foo = NULL; memset(tmpFoo, 0, sizeof(Foo)); free(tmpFoo); } 

你的同事代码不好,因为

  • 如果fooNULL它将会崩溃
  • 创build额外的variables没有意义
  • 将值设置为零是没有意义的
  • 如果它包含必须被释放的东西,直接释放一个结构不起作用

我想你的同事可能想到的就是这个用例

 Foo* a = NULL; Foo* b = createFoo(); destroyFoo(NULL); destroyFoo(&a); destroyFoo(&b); 

在这种情况下,应该是这样的。 在这里尝试

 void destroyFoo(Foo** foo) { if (!foo || !(*foo)) return; free(*foo); *foo = NULL; } 

首先我们需要看看Foo ,让我们假设它看起来像这样

 struct Foo { // variables int number; char character; // array of float int arrSize; float* arr; // pointer to another instance Foo* myTwin; }; 

现在来定义它应该如何销毁,我们首先定义它应该如何创build

 Foo* createFoo (int arrSize, Foo* twin) { Foo* t = (Foo*) malloc(sizeof(Foo)); // initialize with default values t->number = 1; t->character = '1'; // initialize the array t->arrSize = (arrSize>0?arrSize:10); t->arr = (float*) malloc(sizeof(float) * t->arrSize); // a Foo is a twin with only one other Foo t->myTwin = twin; if(twin) twin->myTwin = t; return t; } 

现在我们可以写一个与创build函数相反的破坏函数

 Foo* destroyFoo (Foo* foo) { if (foo) { // we allocated the array, so we have to free it free(t->arr); // to avoid broken pointer, we need to nullify the twin pointer if(t->myTwin) t->myTwin->myTwin = NULL; } free(foo); return NULL; } 

testing在这里尝试

 int main () { Foo* a = createFoo (2, NULL); Foo* b = createFoo (4, a); a = destroyFoo(a); b = destroyFoo(b); printf("success"); return 0; }