在C中,函数可以返回指向该函数动态分配的内存的指针,并要求调用代码释放它。要求调用代码向第二个函数提供缓冲区也是很常见的,然后第二个函数设置该缓冲区的内容。例如:
struct mystruct {
int a;
char *b;
};
struct mystruct *get_a_struct(int a, char*b)
{
struct mystruct *m = malloc(sizeof(struct mystruct));
m->a = a;
m->b = b;
return m;
}
int init_a_struct(int a, char*b, struct mystruct *s)
{
int success = 0;
if (a < 10) {
success = 1;
s->a = a;
s->b = b;
}
return success;
}
一种或另一种方法更好吗?我可以想到两者的参数:对于get_a_struct方法,调用代码被简化,因为它只需要 free()返回的结构;对于init_a_struct方法,调用代码很可能无法 free()动态分配内存,因为调用代码本身可能已经分配了它。
答案 0 :(得分:3)
这取决于具体情况,但通常提供分配的缓冲区似乎更可取。
正如Jim所提到的,如果调用函数分配内存,DLL可能会导致问题。如果您决定将代码分发为Dll并且get_a_struct被导出到DLL的用户/可见,则会出现这种情况。然后,用户必须从文档中弄清楚他们是否应该使用免费,删除或其他操作系统特定功能释放内存。此外,即使他们使用正确的函数来释放内存,他们也可能使用不同版本的C / C ++运行时。这可能导致很难找到的错误。检查this Raymond Chen发布或搜索“内存分配dll边界”。典型的解决方案是从DLL导出您自己的免费功能。所以你将拥有这对:get_a_struct / release_a_struct。
另一方面,有时只有被调用的函数知道需要分配的内存量。在这种情况下,被调用函数进行分配更有意义。如果那是不可能的,比如因为DLL边界问题,一个典型的虽然丑陋的解决方案是提供一种机制来查找这些信息。例如,在Windows中,如果传递0和NULL作为参数,GetCurrentDirectory函数将返回所需的缓冲区大小。
答案 1 :(得分:1)
我认为提供已经分配的结构作为参数是更可取的,因为在大多数情况下,您不需要在调用代码中调用malloc / calloc,因此担心会释放它。例如:
int init_struct(struct some_struct *ss, args...)
{
// init ss
}
int main()
{
struct some_struct foo;
init_struct(&foo, some_args...);
// free is not needed
}
答案 2 :(得分:1)
“优先传递指针”,除非出于某些后勤原因绝对要求每个对象都是“从堆分配的新对象”,例如它将作为节点放入链接列表中,并且链接列表处理程序最终将通过调用free来破坏元素 - 或者“从此处创建的所有内容稍后将转到free
”的其他情况。
请注意,如果可能,“不调用malloc
”始终是首选解决方案。调用malloc
不仅需要一些时间,它还意味着某个地方,你必须在分配的内存上调用free
,并且每个分配的对象需要几个字节(通常为12-40字节) “开销” - 所以为小物件分配空间绝对是浪费。
答案 3 :(得分:0)
我同意其他答案,传递分配的struct
是首选,但有一种情况可能首选返回指针。
如果您需要在最后显式释放某些资源(关闭文件或套接字,或者释放结构内部的某些内存,或者加入一个线程,或者需要在C ++中使用析构函数的其他内容),我认为在内部分配,然后返回指针可能更好。
我认为是这样的,因为在C中,它意味着某种合同:如果你分配自己的struct
,你不应该做任何事情来摧毁它,它会自动清除功能结束。另一方面,如果你收到一些动态分配的指针,你会觉得有必要在最后调用一些东西来销毁它,而destroy_a_struct
函数就是你需要进行其他清理任务的地方{{1} }。