【问题标题】:Freeing memory returned from C functions释放从 C 函数返回的内存
【发布时间】:2009-05-26 16:14:20
【问题描述】:

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

  • 提供一个“析构函数”来封装对 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);

【问题讨论】:

  • 你的最后一个例子('FILE f=&fopen("blah", "w")')是假的。不要这样做。
  • 至少确保你发布了可编译的代码

标签: c file free


【解决方案1】:

分配和释放内存的最佳选择是对称地进行。即如果调用者分配内存,让调用者释放它。如果您的 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

【讨论】:

  • +1,因为您没有像其他人那样浪费您/我们的时间来误解他的问题。
【解决方案2】:

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

话虽如此,至于你最初的问题:

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

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

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

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

【讨论】:

  • 谢谢,这些理由清楚地表明提供“析构函数”是可取的。
【解决方案3】:

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

【讨论】:

  • 我假设内存在 fopen 中是 malloc 的。
  • 抱歉 malloc/free (C++ 中的 new/delete) 同样的规则适用,如果 fopen() malloced 则 fclose() 负责
  • 这一点必须与 MGB 达成一致。
  • @fexii,经典的 fopen() 返回一个指向静态分配的 FILE 对象数组的元素的指针。没有调用 malloc()。这就是为什么标准说 fopen() 将至少支持 FOPEN_MAX 并发打开文件。
【解决方案4】:

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

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

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

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

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

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

【讨论】:

  • 您是在解决示例,而不是问题。
【解决方案5】:

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

【讨论】:

  • 您是在解决示例,而不是问题。
【解决方案6】:

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

【讨论】:

    【解决方案7】:

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

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

    【讨论】:

      【解决方案8】:

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

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

      open/allocate 函数应该返回某种类型的不透明句柄,该句柄由 close 函数关闭/解除分配。

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

      【讨论】:

        【解决方案9】:

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

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

        【讨论】:

          【解决方案10】:

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

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

          【讨论】:

            猜你喜欢
            • 2021-05-25
            • 2018-07-29
            • 2017-09-03
            • 2020-12-24
            • 1970-01-01
            • 2012-07-29
            • 2017-01-10
            • 1970-01-01
            • 2018-04-15
            相关资源
            最近更新 更多