关于free()如何在C中起作用的澄清 -

时间:2012-11-30 12:38:22

标签: c

我的问题是关于何时使用free()在C中是合适的。我正在使用gcc 4.3.2。

假设,如果必须在链表中释放一堆内存,理想的方法就是(我猜):

int freeLL(node *a)
{
    if(a->next != NULL)
         freeLL(a->next);
    free(a);
    return 0;
}

现在,假设我在下面的ADT上做了类似的事情:

一个指针“VertexNode”,它还有两个指针:“Vertex”和“Edge”(比如说)。相当于说:

struct vertexnode
{
     vertex *v;
     edge *e;
}
typedef struct vertexnode* VertexNode;

稍后,在初始化实例时,我会做类似的事情 -

VertexNode V = malloc(sizeof(struct vertexnode));
V->v = malloc(sizeof(vertex));

所以,最终在释放时:我使用了与链接列表中使用的相同的类比。

free(V->v);
free(V);

这给出了运行时错误,当我注释掉“free(V-> v)”时,程序运行正常。 我的问题是:

a)简单地做免费(V)是否足够?我的意思是,free()是否在给定指针内的所有指针上递归工作?

b)如果没有,在这种情况下是否存在内存泄漏?我如何理想地防止这种情况?

c)最后,有没有办法可以跟踪malloc()和分配的字节数 有多少人被free()释放了?

对于这个冗长的问题,我感到非常抱歉。提前感谢您的时间和耐心。

6 个答案:

答案 0 :(得分:2)

不,free不会递归工作,因此你确实会有内存泄漏。您正在发生的运行时错误可能是一个逻辑错误(可能V->vNULL,或者您在释放之前没有分配它。)

如果您正在使用linux,使用valgrind可以帮助您分析程序并提及泄漏错误。使用cc *.c -ggdb进行编译然后运行valgrind --leakcheck=full ./a.out将输出泄漏错误。

答案 1 :(得分:2)

回答你的问题:

a)否.free()不会递归释放struct的成员指针。

b)是的,在这种情况下存在内存泄漏。你必须用你的代码释放所有已分配的内存。

c)您可以使用工具检查内存泄漏,例如valgrind,这很容易。我知道有些项目实现了自己的内存管理,它们将malloc和free包装在自己的API中,以便您可以跟踪其API中的内存使用情况。

答案 2 :(得分:2)

对于完整的内存管理,我会有一个内存池。这将消除为每个被调用的malloc调用一些free的所有痛苦。我使用Apache Portable运行时(APR)来进行内存池。只需在开始时分配一块内存来初始化。为每个指针分配尽可能多的内容。然后在最后只需拨打一个电话即可释放所有内存。这比使用大量mallocs和释放导致内存泄漏的效率要高得多。

作为旁注。如果你不使用内存池,我建议你使用valgrind来测试你的应用程序。实际上你应该总是使用valgrind。

答案 3 :(得分:1)

问题是:您需要确保您尝试释放的指针实际上已初始化。

在调用V->v之前,我还会检查NULL是否不是free(请参阅我对您的问题的评论)。

free不是“递归”。如果你没有释放ev(在你的例子中),你就会泄露内存。

答案 4 :(得分:1)

答:free()递归不起作用。   - 如果您使用的是ADT概念,那么我建议您通过创建createVertexNode()函数在类型内部执行分配,并在freeVertexNode()函数内执行检查和取消分配

B:如果你无法释放它,那么它将是内存泄漏   - 你可以通过确保你的ADT功能检查并释放它分配的内存来避免它,或者......

C:使用内存泄漏检查程序。 Visual Studio有一个内置的,或使用其他如valgrind或Rational Purify。我敢肯定有更多的免费开源库,最简单的是,它们覆盖了malloc()和free()调用

答案 5 :(得分:1)

基本上要记住的是对malloc() / free()的调用应为1:1。如果我打电话给malloc()来获取一些记忆,我需要打电话给free()以便在我完成后返回。

这回答问题a),不,它不是递归的。 (或者对free()的一次调用就足够了)。

b)你打赌会有内存泄漏!想想一些简单的代码:

typedef struct pointless {
    struct pointless * next;
}pl;

pl * head = malloc(sizeof(pl)); // head gets 4 bytes
pl->next = malloc(sizeof(pl));  // head->next gets 4 bytes
free(head);  // head's allocated 4 bytes are deleted

显然这是一个毫无意义的列表,因为它只是指向下一个空元素,但它说明了这一点。我的总列表分配了8个字节(head为4个,headnext为4个)。当我调用free()时,它可以在head的4个字节上运行,但就是这样。

以这种方式工作是件好事!考虑从链表中间删除单个项目,您想要释放该节点的内存,但不是整个内存!但是,根据我上面的示例,由于head->next未被释放,因此会出现内存泄漏;并且在您致电free(head)后,无法保证您可以再访问head->next。当然可能......但UB此时此刻。

c)该级别的内存管理由操作系统完成。如果你想跟踪你分配/释放了多少你必须做的事情:

int total_mem = 0;

head = malloc((total_mem += sizeof(pl)));

free(head);
total_mem -= sizeof(head);

当然你可以在malloc()free()调用周围放置包装器来为你做数学处理,这样可以更容易管理,但你明白了。