1

In a separate library, we have a struct with:

typedef struct bsmat{
int m;
int *n;
double **d;
} bs;

where **d is a array of pointers to double arrays.

bs *new_bs(int n, double **d);

There are two use cases:

(a) The main application allocates multiple double matrices and calls the library to construct the structure.

b = new_bs(5, p)

(b) Alternatively, an object may be generated by the library as a result of a call to a function:

bs *add(bs *a, bs *b);

In the second case, the library owns **d and can free it when necessary. In the first case, the application has allocated the array and the library may read from/write to it. It is not clear to me as to who should free what and when??

Is it a problem to allow the main application to own a struct-member? What are the problems with this approach? What are the alternatives? We are trying to avoid copying large number of arrays back-and-forth.

thanks

TR

4

2 回答 2

3

一般来说,图书馆记录它与使用其服务的应用程序的“合同”是很重要的。库编写者记录了库所做的所有分配以及应用程序需要释放的内容。像任何合同一样,简单且一致的合同很好。

在这种特殊情况下,我想图书馆还应该提供:

void free_bs(bs *b) 
{
     if (b->d) {
          /* free b->d here */
     }
     /* free rest of the structure */
}

这释放了结构。在这里也释放 d 数组是有意义的。应用程序具有指向所有bs结构的指针,并负责free_bs在不再需要时调用它们。

例如,如果您想保留 d 以供将来使用,则在完成库操作后,合同会稍微复杂一些。图书馆还可以提供:

double** bs_get_data(bs *b)
{
     double **d = b->d;
     b->d = NULL;
     return b->d;
}

它返回一个指向 的指针d,并将该字段留空,以便自由例程知道跳过它。

文档bs_get_data()必须解释它只能被调用一次,并且在这种情况下应用程序负责释放数组。

更新: 在回答您下面的评论时:首先,请注意,我通过假设(至少)以下内容简化了问题:d由单个bs结构或应用程序引用。应用程序和库将一个对数组的引用从一个“传递”到另一个。例如,如果您希望d在更多结构中使用相同的数组,那么bs我的方法是不够的。

您在评论中提出的标志可能会有所帮助,但不是标准做法,FWIK。在这种情况下,我建议实现一些简单的引用计数。如果d是需要共享的资源,请将其设为“对象”:

 typedef struct {
         double **d;
         int ref;
 } d_t;

ref用 1初始化。在new_bs()所有接收“副本”的函数中d,增加引用计数。当结构被删除时,或者当您的应用程序bs不再需要时,减少它的引用计数。d如果它变为零,则释放它。在某种程度上,这是高级语言为您做的事情,并且在保持资源管理正常方面非常有效。

所以没有人拥有该阵列,但谁最后需要它,谁就释放它。

当然,这需要更多的工作和复杂的代码,所以尽量保持平衡。您拥有的每个结构都不需要它,但仅适用于您需要保留多个引用的结构。

于 2009-08-05T23:03:52.157 回答
1

我认为引用计数对于这个问题来说太过分了。但是您的界面需要更准确地指定谁拥有*b,而不仅仅是b->d. 我完全不清楚图书馆如何知道何时“必要”(如您所说) free b->d

我将接口扩展如下:

  1. 每个类型的指针bs *要么是外部管理的,要么是内部管理的。函数new_bs返回一个外部管理的指针;函数add返回一个内部管理的。

  2. 每个返回类型指针的函数都bs *应该说明结果的管理是内部的还是外部的。

  3. 您可能应该提供功能

    void bs_free_ext(bs **);  // free *bs and set *bs = NULL (external mgmt)
    void bs_free_int(bs **);  // free *bs and set *bs = NULL (internal mgmt)
    

    bs_free_ext仅释放*bsbs_free_int释放*bs(*bs)->d

  4. 为了安全起见,我还将添加记录每个结构的存储管理的字段,并且我将对任何分配或释放的函数进行断言。(原因:与分配或释放的成本相比,断言检查成本低。)

如果您可以在 Linux 上运行您的应用程序,那么使用valgrind进行内存检查是一个加分项。

PS 内存管理细节泄漏到几乎每个接口中的事实是我们为获得 C 编程的好处而付出的代价之一。

于 2009-08-06T02:52:25.180 回答