使用free()释放内存的正确方法和带返回值的C语言方法

3
我在程序中有几个方法,其中我使用char *str,我对其进行了动态内存分配(malloc),并需要在方法结束时返回str。但是我不知道在这些方法中应该在哪里放置free()语句。如果我在返回str之前释放它,那么str将为空,如果我在return语句之后释放它,它可能会在错误的时间被释放。如果在释放之前退出程序,这将导致内存泄漏。正确的做法是什么?

3
接收指针的代码应该在某个地方释放内存。当你完成使用时,简单地释放内存即可。 - Some programmer dude
1
如果您的函数正在分配内存,将其填充为有用的内容并返回指向它的指针,则应该记录下来,该指针应由调用代码释放。 - Eugene Sh.
当您不再使用str时,应该释放它。 - anothertest
为什么你会在free()之前让程序退出?这不是一个好的设计方法。 - muradin
6个回答

5
阅读你的问题,我认为你对malloc和free的工作原理有误解。
Malloc将在进程的堆上分配空间。这个堆是全局的,意味着你可以从任何代码中访问它(而不仅仅是在函数中)。因此,你也可以在任何代码位置释放它。如果你返回一个指向已分配空间的指针,在同一个函数中释放它就没有意义了(否则你会得到一个悬空指针,即指向已经释放的空间)。所以在你的函数之外释放指针是完全正确(也是必要的)的做法。
此外,像你所说的那样,你的进程退出后并不存在内存泄漏问题。内存泄漏发生在进程内部,意思是分配了一些进程的堆,但不再使用(并且没有引用它,因此无法释放)。当程序占用大量堆空间时,这可能导致内存不足问题(而且这对性能来说也不好,所以你永远都不希望发生这种情况)。 然而,当进程退出时,所有的内存(包括堆)都会被操作系统回收,因此不再存在内存泄漏(除非你正在处理多线程,但这是另一个话题)。

2
谢谢你的好解释!你说得对,我还没有完全理解它的工作原理,但现在我感觉自己理解了一点点 :) - user16655

2

如果我的代码能够遵循这个模式,我通常会感到非常高兴:

char * foo_allocate() {
    char * s = malloc(10);
    snprintf(s, 10, "hello");
    return s;
}

void foo_release(char *s) {
   free(s);
}

谢谢!我没有想到我可以从程序的另一个方法/位置释放它。 - user16655
在这种情况下,asprintf()非常好用:它会自动为您分配足够的内存。不需要使用固定的缓冲区大小,这样您就不会出错或者随着使用量的增加而变得不足...您还可以提到,应该记录下调用者需要释放缓冲区的信息。 - cmaster - reinstate monica
@cmaster:这里的snprintf()只是一个例子。也许数据来自套接字,或者来自其他任何地方... - Bill Lynch

1

当您不再需要缓冲区的内容时,必须释放该缓冲区,而不是在之前释放。

您的模式应该像这样:

char *SomeFunction()
{
   ...
   return malloc(somelength) ;
}

...
char *mychar = SomeFunction() ;
... // deal with the mychar buffer

free(mychar);
...

0

这个问题有很多替代方案,以下是一些解决方案:

1- 使用一个函数来分配内存,另一个函数来释放内存,就像Bill Lynch在他的回答中所说的那样。当您使用良好的实践(创建构造函数和析构函数)时,通常会使用此方法。

2- 使用该函数的人负责释放它返回的字符串。

3- 您可以将返回值作为参数传递,以下是一个示例:

void foo(char* s) {
    snprintf(s, 10, "hello");
}

请注意,当您想返回一个退出代码以表示一切正常时(您会发现许多函数使用此方法),通常会使用最后一种解决方案。
int foo(char* s) {
        snprintf(s, 10, "hello");
        return 1; // everything went well
}

这样的用户提供的缓冲区通常是灾难的根源。最好返回一个适当的 malloc() 缓冲区。 - cmaster - reinstate monica

0
在使用分配的内存的最高级别函数中,调用free()来释放内存。
在释放后,将值设置为NULL可以确保后续使用s不会使用已释放的内存。如果变量没有随后被访问,则编译器可能会优化掉不需要的s = NULL;
char *Allocator() {
   ...
   char *s = malloc(...);
   ...
   return s;
}


void foo() {
  ...
  char *s = Allocator;
  // ... use s
  free(s);
  s = NULL;
  ...
}

0

释放已分配的内存的位置并不重要,只要您不要忘记释放它即可。在我看来,最好的地方是在函数返回之前,在一个名为cleanup的标签中放置free()。

这样,您就不会有内存泄漏问题,因为如果您确保每个函数都没有内存泄漏,那么整个程序也不会有内存泄漏。

您的函数应该像这样:

int func(void)
{
    /* Code that has allocations in it  */
cleanup:
    /* Here you should free all of your allocations */
    return 0;
}

如果您正在使用Linux,可以使用valgrind来确保您没有任何内存泄漏。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接