返回一个指向动态分配结构的指针,而不需要调用函数分配的内存?

时间:2013-07-23 00:49:16

标签: c memory struct

在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()动态分配内存,因为调用代码本身可能已经分配了它。

4 个答案:

答案 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} }。