结构中的共享对象:调用程序和库之间(在 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
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
一般来说,图书馆记录其与使用其服务的应用程序签订的“合同”非常重要。 库编写者记录了库所做的所有分配以及应用程序需要释放的内容。 与任何合同一样,简单且一致的合同就是好的。
在这种特殊情况下,我认为图书馆还应该提供:
释放结构。 在这里释放 d 数组也是有意义的。 应用程序具有指向所有
bs
结构的指针,并负责在不再需要时调用它们的free_bs
。例如,如果您想保留 d 以供将来使用,则在完成库操作后,合同会稍微复杂一些。 该库还可以提供:
返回一个指向 d 的指针,并将该字段留空,以便自由例程知道要跳过它。
bs_get_data()
的文档必须解释它只能被调用一次,并且在这种情况下应用程序负责释放数组。更新:
回答您下面的评论:首先,请注意,我通过假设(至少)以下内容简化了问题:
d
由单个bs
引用结构或应用程序。 应用程序和库将对数组的单个引用从一个传递到另一个。 例如,如果您希望在更多bs
结构中使用相同的d
数组,那么我的方法还不够。您在评论中提出的标志可能会有所帮助,但不是标准做法,FWIK。 在这种情况下,我建议实现一些简单的引用计数。 如果
d
是需要共享的资源,请将其设为“对象”:在
new_bs()
中将ref
初始化为 1。在所有接收d
“副本”的函数中,增加引用计数。 当bs
结构被删除时,或者当您的应用程序中不再需要d
时,请减少它的引用计数。 如果它为零,则释放它。 在某种程度上,这就是高级语言为您所做的事情,并且在保持资源管理理智方面非常有效。因此没有人拥有该数组,但最后需要它的人可以释放它。
当然,这需要更多的工作并使代码变得复杂,因此请尽量保持平衡。 并不是您拥有的每个结构都需要它,而只是您需要保留多个引用的结构才需要它。
In general, it's important that the library documents the 'contract' it makes with the applications that are using its service. The library writer documents all the allocations made by the library and what needs to be freed by the application. Like any contract, it's good when it is simple and consistent.
In this particular case, I suppose the library should also offer:
which frees the structure. It makes sense to also free the d array here. The application has pointer to all the
bs
structures and is responsible for callingfree_bs
on them when no longer needed.If, for example, you want to keep d for future usage, after you're done with the library operation, the contract is a bit more complex. The library could also provide:
which returns a pointer to
d
, and leaves the field empty so that the free routine knows to skip it.The docs for the
bs_get_data()
have to explain that it can be called only once, and that the application takes the responsibility of freeing the array in this case.UPDATE:
In answer to your comment below: First of all, please note that I simplified the problem by assuming (at least) the following: the
d
is referenced either by a singlebs
structure or by the application. The application and the library "pass" a single reference to the array from one to the other. If you want the samed
array in morebs
structures, for example, than my approach is not enough.The flag that you propose in the comment might help, but is not standard practice, FWIK. In this case, I would suggest to implement some simple reference counting. If
d
is the resource that needs to be shared around, make it an "object":Initialize
ref
with 1. Innew_bs()
and in all functions that receive a "copy" ofd
, increment the reference count. When thebs
structure gets deleted, or when you no longer needd
in your application, decrement it's reference count. If it gets to zero, free it. In a way, it is what the high level languages do for you, and is very efficient in keeping the resource management sane.So no one is owning the array, but whoever needs it last, frees it.
Of course, this requires more work and complicates code, so try to put in balance. It's not needed for every structure you have, but only for those you need to keep multiple references to.
我认为引用计数对于这个问题来说太过分了。 但是您的界面需要更准确地指定谁拥有
*b
而不仅仅是b->d
。 我根本不清楚图书馆如何知道何时“有必要”(如您所说)释放b->d
。我将按如下方式扩展接口:
每个
bs *
类型的指针要么外部管理,要么内部管理。 函数new_bs
返回一个外部管理的指针; 函数add
返回一个内部管理的指针。Every 返回
bs *
类型指针的函数应该说明结果的管理是内部还是外部。你可能应该提供函数
bs_free_ext
仅释放*bs
;bs_free_int
释放*bs
和(*bs)->d
。为了安全起见,我还会添加记录每个结构的存储管理的字段,并且我会对任何分配或释放的函数进行断言。 (原因:与分配或释放的成本相比,断言检查便宜。)
如果您可以在 Linux 上运行应用程序,则使用 进行内存检查valgrind 是一个优点。
PS 内存管理的细节泄漏到几乎每个接口中这一事实是我们为了获得 C 语言编程的好处而付出的代价之一。
I think reference counting is overkill for this problem. But your interface needs to specify more precisely who owns
*b
and not justb->d
. It's not at all clear to me how the library knows when it is "necessary" (as you put it) to freeb->d
.I'd extend the interface as follows:
Every pointer of type
bs *
is either externally managed or internally managed. Functionnew_bs
returns an externally managed pointer; functionadd
returns an internally managed one.Every function that returns a pointer of type
bs *
should say whether the management of the result is internal or external.You probably should provide functions
bs_free_ext
frees only*bs
;bs_free_int
frees both*bs
and(*bs)->d
.For safety's sake I would also add the field that records the storage management of each struct, and I would have assertions on any function that allocates or frees. (Reason: assertion checking is cheap compared to the cost of allocating or freeing.)
If you can run your app on Linux then memory checking with valgrind is a plus.
P.S. The fact that details of memory management leak into almost every interface is one of the costs we pay to get the benefits of programming in C.