释放从C函数返回的内存

时间:2009-05-26 16:14:21

标签: c file free

在C中,这是释放函数返回的内存时更好的做法:

  • 提供一个“析构函数”函数,封装对free()的调用。
  • 要求用户自己释放()返回的指针。

例如,要打开和关闭我们执行的文件:

FILE* f = fopen("blah", "w");
fclose(f);

这更适合:

FILE* f = fopen("blah", "w");
fclose(f);
free(f);

警告:不要在FILE指针上调用free()。我只在这里使用它假设的实现。

那么局部变量指向返回的内存的情况呢?免费()有害吗? (或者也许永远不应该这样做)

FILE f = &fopen("blah", "w");
fclose(&f);

10 个答案:

答案 0 :(得分:14)

分配和释放内存的最佳选择是对称地执行此操作。即如果调用者分配内存,让调用者释放它。如果您的API分配内存(被调用者),那么您的API应该释放它。

来电者分配/免费示例:

int * mymem = (int *)malloc(20 * sizeof(int));
...
a_func_to_call(mymem);
...
free(mymem);

callee alloc / free的示例:

FILE* f = fopen("blah", "w"); // allocs a FILE struct
fclose(f); // The implementation of fclose() will do what's necessary to 
           // free resources and if it chooses to deallocate any memory
           // previously allocated

答案 1 :(得分:10)

你永远不应该释放文件 - fclose处理正确释放资源。通常,只有malloc直接分配的自由指针。大多数其他指针都有自己的资源清理功能。

话虽如此,关于你的初步问题:

我发现提供析构函数通常是更好的做法,原因有三个。

1)在很多情况下,免费是不合适的,这对您的最终用户来说可能并不明显。 FILE *就是一个很好的例子 - 你不应该在{...}上面调用free(f); ...

2)如果你在DLL中使用它,根据运行时,封装的免费功能可以解决混合运行时的许多细微错误,特别是在Windows平台上。尝试在VS2008中使用VS2005中编译的DLL,如果碰巧在一个平台的代码中释放内存,则会导致问题。具有“包装”功能来处理内存管理可以解决这个重要问题。

3)许多C API函数都以这种方式工作 - 例如使用fopen / fclose的FILE *。这对您图书馆的用户来说并不奇怪。

答案 2 :(得分:5)

你不能释放FILE *它不是由malloc分配的。
由于您不负责分配它 - 您不应该释放它。

答案 3 :(得分:2)

FILE* f是指向FILE对象的指针,该对象用于在涉及文件的所有其他操作上标识流。您不应使用free(f),因为malloc().

未分配内存

fclose就足以关闭与流关​​联的文件。

关于提供析构函数释放内存的函数的问题: 我觉得如果函数不仅仅是释放内存,那么提供一个析构函数是合适的。

wrapperFree(Pointer* p)
{
 //do some additional work [ other cleanup operations ]
 free(p);
}

另外,需要提供与WrapperFree关联的WrapperAllocate。

否则,我认为这个问题会明确地针对每个malloc()来电free()

答案 4 :(得分:0)

fopen返回指向C库内部管理FILE结构的指针,该结构通过调用free而被释放(不一定是fclose d。)

答案 5 :(得分:0)

我猜你的意思是malloc()而不是fopen()。 malloc()分配内存并返回已分配内存的地址。必须使用free()调用发布此内容。

答案 6 :(得分:0)

正如mgb所说,没有必要free()一个FILE *。但在典型的使用中,存在库调用和您自己的代码,它们将分配内存并使您负责。以下是一些宽松的指导原则:

  • 除了free内存之外需要发生某些事情时,创建一个“析构函数”函数。 FILE *的东西就是一个很好的例子:它必须处理文件本身以及处理内存。
  • 当它只是一块内存时使用裸free()。其他任何事都是矫枉过正。

答案 7 :(得分:0)

没有上下文就没有最佳实践......

但我要说信息隐藏的原则意味着函数使用的内存分配和释放的细节通常应该封装在函数中。

open / allocate函数应该返回一个由close函数关闭/取消分配的某种不透明句柄。

FILE *可以被认为是一个不透明的句柄。

答案 8 :(得分:0)

如前所述,将“内务”代码包装在相应的函数(即构造函数/析构函数)中通常是很好的做法,特别是在处理异常处理时,甚至在指定自己的出口/信号处理程序时,它也可能很有用。 :

在这种情况下,使用析构函数帮助程序非常有用,因为您只需使用atexit()等函数注册它们,以便在程序终止时自动调用它们,以便可以正确清理全局状态。 / p>

答案 9 :(得分:0)

我不仅喜欢析构函数,而且喜欢Dave Hanson的约定,它采用指针的地址并在释放内存时将指针清零:

Thing_T *Thing_new(void);
void Thing_dispose(Thing_T **p);   // free *p and set *p = NULL