[英]shared objects within a struct: between a calling program and library (in c)
在一个单独的库中,我们有一个结构:
typedef struct bsmat{
int m;
int *n;
double **d;
} bs;
其中** d是指向双数组的指针数组。
bs *new_bs(int n, double **d);
有两种用例:
(a)主应用程序分配多个双矩阵并调用库来构造结构。
b = new_bs(5, p)
(b)或者,作为调用函数的结果,库可以生成对象:
bs *add(bs *a, bs *b);
在第二种情况下,图书馆拥有** d并且可以在必要时释放它。 在第一种情况下,应用程序已分配数组,并且库可以读取/写入它。 我不清楚谁应该释放什么,什么时候?
允许主应用程序拥有结构成员是一个问题吗? 这种方法有什么问题? 有哪些替代方案? 我们试图避免来回复制大量数组。
谢谢
TR
一般而言,图书馆记录它与使用其服务的应用程序所做的“合同”非常重要。 库编写器记录了库所做的所有分配以及应用程序需要释放的内容。 像任何合同一样,当它简单而一致时很好。
在这种特殊情况下,我认为图书馆还应该提供:
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
结构或应用程序引用。 应用程序和库将一个引用从一个引用“传递”到另一个引用。 例如,如果你想在更多的bs
结构中使用相同的d
数组,那么我的方法还不够。
您在评论中提出的标志可能有所帮助,但不是标准做法,FWIK。 在这种情况下,我建议实现一些简单的引用计数 。 如果d
是需要共享的资源,请将其设为“对象”:
typedef struct {
double **d;
int ref;
} d_t;
使用1初始化ref
。在new_bs()
和接收d
的“副本”的所有函数中,增加引用计数。 当bs
结构被删除时,或者当你的应用程序中不再需要d
时,减少它的引用计数。 如果它变为零,则释放它。 在某种程度上,它是高级语言为您所做的事情,并且非常有效地保持资源管理的合理性。
所以没有人拥有阵列,但无论谁最后需要它,都可以释放它。
当然,这需要更多的工作并使代码复杂化,所以尽量保持平衡。 您拥有的每个结构都不需要它,但仅适用于您需要保留多个引用的结构。
我认为引用计数对于这个问题来说太过分了。 但是你的界面需要更准确地指定谁拥有*b
而不仅仅是b->d
。 我完全不清楚图书馆知道什么时候“必要”(如你所说)释放b->d
。
我将扩展界面如下:
bs *
类型的每个指针都是外部管理或内部管理的 。 函数new_bs
返回一个外部管理的指针; function add
返回内部管理的。
返回类型为bs *
的指针的每个函数都应该说明结果的管理是内部还是外部。
你可能应该提供功能
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
只释放*bs
; bs_free_int
释放*bs
和(*bs)->d
。
为了安全起见,我还要添加记录每个结构的存储管理的字段,并且我会对任何分配或释放的函数进行断言。 (原因:与分配或释放的成本相比,断言检查很便宜。)
如果您可以在Linux上运行您的应用程序,那么使用valgrind进行内存检查是一个优势。
PS内存管理的细节几乎渗透到每个界面这一事实是我们为了获得C语言编程的好处而付出的代价之一。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.