Tor*_*dek 52 c api malloc memory-management
在C API中分配内存的正确/首选方法是什么?
我首先看到两个选项:
1)让调用者完成所有(外部)内存处理:
myStruct *s = malloc(sizeof(s));
myStruct_init(s);
myStruct_foo(s);
myStruct_destroy(s);
free(s);
Run Code Online (Sandbox Code Playgroud)
该_init和_destroy功能是必需的,因为一些更多的内存可分配里面,而且必须在某个地方处理.
这样做的缺点是更长,但在某些情况下也可以消除malloc(例如,它可以传递给堆栈分配的结构:
int bar() {
myStruct s;
myStruct_init(&s);
myStruct_foo(&s);
myStruct_destroy(&s);
}
Run Code Online (Sandbox Code Playgroud)
此外,调用者必须知道结构的大小.
2)隐藏mallocs in _init和freein in _destroy.
优点:代码更短,因为无论如何都会调用函数.完全不透明的结构.
缺点:无法传递以不同方式分配的结构.
myStruct *s = myStruct_init();
myStruct_foo(s);
myStruct_destroy(foo);
Run Code Online (Sandbox Code Playgroud)
我目前正在倾向于第一个案例; 然后,我不知道C API设计.
Pav*_*aev 15
#2的另一个缺点是调用者无法控制事物的分配方式.这可以通过为客户端提供API来注册他自己的分配/释放函数(如SDL)来解决,但即使这样也可能不够精细.
#1的缺点是当输出缓冲区不是固定大小(例如字符串)时它不能很好地工作.最好,您需要提供另一个函数来首先获取缓冲区的长度,以便调用者可以分配它.在最坏的情况下,根本不可能有效地这样做(即,在一个单独的路径上计算长度比一次计算和复制过于昂贵).
#2的优点是它允许您严格地将数据类型公开为不透明指针(即声明结构但不定义它,并且一致地使用指针).然后,您可以在库的未来版本中更改结构的定义,而客户端在二进制级别上保持兼容.使用#1,您必须通过要求客户端以某种方式在结构中指定版本(例如cbSize,Win32 API中的所有这些字段),然后手动编写可以处理结构的较旧版本和较新版本的代码来执行此操作.随着您的库的发展,保持二进制兼容.
一般来说,如果你的结构是透明的数据,随着未来对图书馆的小修改不会改变,我会选择#1.如果它是一个或多或少复杂的数据对象,并且您希望完全封装以防万一,以便将来开发,请使用#2.
Jer*_*myP 15
每次方法编号为2.
为什么?因为使用方法编号1,您必须将实现详细信息泄漏给调用者.调用者必须至少知道结构的大小.如果不重新编译使用它的任何代码,则无法更改对象的内部实现.
Sec*_*ure 12
为什么不提供两者,以充分利用这两个世界?
使用_init和_terminate函数来使用方法#1(或任何你认为合适的命名).
使用其他_create和_destroy函数进行动态分配.由于_init和_terminate已经存在,它有效归结为:
myStruct *myStruct_create ()
{
myStruct *s = malloc(sizeof(*s));
if (s)
{
myStruct_init(s);
}
return (s);
}
void myStruct_destroy (myStruct *s)
{
myStruct_terminate(s);
free(s);
}
Run Code Online (Sandbox Code Playgroud)
如果你想让它不透明,那么make _init和_terminate static并且不要在API中公开它们,只提供_create和_destroy.如果您需要其他分配,例如使用给定的回调,请为此提供另一组函数,例如_createcalled,_destroycalled.
重要的是跟踪分配,但无论如何你必须这样做.您必须始终使用已使用的分配器的对应部分进行解除分配.
| 归档时间: |
|
| 查看次数: |
6852 次 |
| 最近记录: |