5

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

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

例如,要打开和关闭文件,我们这样做:

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

这是否更可取:

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

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

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

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

10 回答 10

14

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

调用方 alloc/free 示例:

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

被调用者 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
于 2009-05-26T16:21:12.737 回答
10

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

话虽这么说,至于你最初的问题:

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

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

2) 如果您在 DLL 中使用它,则取决于运行时,封装免费功能可以解决混合运行时产生的许多细微错误,尤其是在 Windows 平台上。如果您碰巧在一个平台的代码中释放了在另一个平台上分配的内存,那么尝试使用在 VS2005 中从 VS2008 编译的 DLL 可能会导致问题。拥有一个“包装器”功能来处理内存管理可以解决这个重大问题。

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

于 2009-05-26T16:19:50.853 回答
5

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

于 2009-05-26T16:16:33.793 回答
2

FILE* f是一个指向 FILE 对象的指针,该对象用于在涉及文件的所有进一步操作中标识流。您不应该使用free(f),因为内存不是由malloc().

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

关于您关于提供析构函数来释放内存的问题:我认为如果函数不仅仅释放内存,那么提供析构函数是合适的。

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

此外,对于 WrapperFree 相关的 WrapperAllocate 需要提供。

否则,我认为拇指规则将明确适用于每个malloc()呼叫free()

于 2009-05-26T16:19:18.763 回答
0

fopen返回指向 C 库内部管理FILE结构的指针,该结构free通过调用fclose.

于 2009-05-26T16:18:52.323 回答
0

我猜你的意思是malloc()而不是fopen()。malloc() 分配内存并返回分配内存的地址。这必须使用free()调用释放。

于 2009-05-26T16:21:19.073 回答
0

free()正如 mgb 所说,不需要FILE *. 但在典型使用中,有库调用和您自己的代码将分配内存并让您负责。以下是一些宽松的指导方针:

  • 当除了free内存之外还需要发生某些事情时,请创建一个“析构函数”。这些FILE *东西是一个很好的例子:它必须处理文件本身以及处理内存。
  • free()当它只是一块内存时使用bare 。其他任何事情都是多余的。
于 2009-05-26T16:21:49.773 回答
0

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

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

open/allocate 函数应该返回某种由 close 函数关闭/释放的不透明句柄。

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

于 2009-05-26T16:23:39.537 回答
0

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

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

于 2009-05-26T16:26:56.550 回答
0

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

Thing_T *Thing_new(void);
void Thing_dispose(Thing_T **p);   // free *p and set *p = NULL
于 2009-05-27T02:42:53.810 回答