1

我的问题是关于何时在 C 中使用 free() 是合适的。我使用的是 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) 简单地做 free(V) 就足够了吗?我的意思是,free() 是否对给定指针内的所有指针递归工作?

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

c) 最后,有没有一种方法可以跟踪 malloc() 分配了多少字节以及 free() 释放了多少字节?

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

4

6 回答 6

2

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

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

于 2012-11-30T12:43:28.763 回答
2

要回答您的问题:

a) 不,free() 不会递归地释放结构的成员指针。

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

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

于 2012-11-30T12:47:17.863 回答
2

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

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

于 2012-11-30T13:00:01.623 回答
1

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

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

free不是“递归的”。如果您不释放e并且v(在您的示例中),那么您正在泄漏内存。

于 2012-11-30T12:43:38.420 回答
1

A:free()不能递归工作。- 如果你是 ADT 概念,那么我建议你通过创建一个函数来执行类型内部的分配,并在一个函数createVertexNode()内部进行检查和释放freeVertexNode()

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

C:使用内存泄漏检查器。Visual Studio 有一个内置的,或者使用其他的,例如 valgrind 或 Rational Purify。我确信有更多免费的开源库,最简单的,覆盖 malloc() 和 free() 调用

于 2012-11-30T12:52:16.647 回答
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 个字节(4 个用于head,4 个用于head's next)。当我调用free()它时,它适用于head4 个字节,但仅此而已。

这是一件好事,它以这种方式工作!考虑从链表中间删除单个项目,您想释放该节点的内存,但不是全部!然而,鉴于我上面的例子,你会有一个内存泄漏,因为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()and周围放置包装器free()来为您做数学运算,这将更易于管理,但您明白了。

于 2012-11-30T13:15:37.097 回答