San*_*war 54 c malloc struct pointers
最近在我的C肌肉上工作,并查看我一直在使用它的许多图书馆,这当然让我很好地了解了什么是好的做法.我没见过的一件事是一个返回结构的函数:
something_t make_something() { ... }
Run Code Online (Sandbox Code Playgroud)
从我所吸收的,这是"正确"的方式:
something_t *make_something() { ... }
void destroy_something(something_t *object) { ... }
Run Code Online (Sandbox Code Playgroud)
代码片段2中的体系结构比片段1更受欢迎.所以现在我问,为什么我会直接返回一个结构,就像在代码片段1中一样?当我在两种选择中做出选择时,我应该考虑哪些差异?
此外,该选项如何比较?
void make_something(something_t *object)
Run Code Online (Sandbox Code Playgroud)
Jon*_*rdy 59
什么时候something_t
小(读:复制它就像复制指针一样便宜)并且你希望它默认是堆栈分配的:
something_t make_something(void);
something_t stack_thing = make_something();
something_t *heap_thing = malloc(sizeof *heap_thing);
*heap_thing = make_something();
Run Code Online (Sandbox Code Playgroud)
什么时候something_t
很大或者你想要它分配堆:
something_t *make_something(void);
something_t *heap_thing = make_something();
Run Code Online (Sandbox Code Playgroud)
无论大小如何something_t
,如果您不关心它的分配位置:
void make_something(something_t *);
something_t stack_thing;
make_something(&stack_thing);
something_t *heap_thing = malloc(sizeof *heap_thing);
make_something(heap_thing);
Run Code Online (Sandbox Code Playgroud)
Yak*_*ont 36
这几乎总是关于ABI的稳定性.库版本之间的二进制稳定性.在不是的情况下,有时候会有动态大小的结构.很少是关于超大struct
或性能.
struct
在堆上分配并返回它的速度几乎与返回它的值一样快.该struct
会必须是巨大的.
实际上,速度并不是技术2背后的原因,而是按指针返回,而不是按值返回.
技术2存在ABI稳定性.如果您有一个struct
并且您的下一个版本的库添加了另外20个字段,那么如果它们是预先构造的指针,那么您之前版本的库的使用者是二进制兼容的.超出struct
他们所知的额外数据是他们不必了解的.
如果你在堆栈上返回它,调用者正在为它分配内存,他们必须同意你的大小.如果您的库自上次重建后更新,您将要删除堆栈.
技术2还允许您在返回指针之前和之后隐藏额外数据(将数据附加到结构末尾的版本是变体).您可以使用可变大小的数组结束结构,或者在指针前添加一些额外数据,或者两者都添加.
如果你想struct
在一个稳定的ABI中分配堆栈,那么几乎所有与struct
需要通信的函数都需要传递版本信息.
所以
something_t make_something(unsigned library_version) { ... }
Run Code Online (Sandbox Code Playgroud)
library_version
库使用where 来确定something_t
它应返回的版本,并更改它操作的堆栈的大小.使用标准C是不可能的,但是
void make_something(something_t* here) { ... }
Run Code Online (Sandbox Code Playgroud)
是.在这种情况下,something_t
可能有一个version
字段作为其第一个元素(或大小字段),并且您需要在调用之前填充它make_something
.
其他库代码something_t
然后查询该version
字段以确定something_t
它们正在使用的版本.
Lun*_*din 13
根据经验,您不应该struct
按值传递对象.实际上,只要它们小于或等于CPU在单个指令中可以处理的最大大小,就可以这样做.但在风格上,人们通常会避免它.如果您从未按值传递结构,则稍后可以向结构添加成员,这不会影响性能.
我认为这void make_something(something_t *object)
是在C中使用结构的最常用方法.您将分配留给调用者.它很有效但不漂亮.
但是,面向对象的C程序使用,something_t *make_something()
因为它们是使用opaque类型的概念构建的,这会强制您使用指针.返回的指针指向动态内存还是其他内容取决于实现.具有opaque类型的OO通常是设计更复杂的C程序的最优雅和最好的方法之一,但遗憾的是,很少有C程序员知道/关心它.
第一种方法的一些优点:
free
.一些缺点: