释放图表时释放太多

时间:2012-10-19 17:47:16

标签: c memory-management valgrind

我使用链接列表创建了图表数据结构。使用此代码:

typedef struct vertexNode *vertexPointer;
typedef struct edgeNode *edgePointer;

void freeGraph(vertexPointer); /* announce function */

struct edgeNode{
    vertexPointer connectsTo;
    edgePointer next;
};

struct vertexNode{
    int vertex;
    edgePointer next;
};

然后我创建一个图形,其中我有4个节点,比如A,B,C和D,其中: A通过B连接到D,A通过C连接到D.通过链接列表,我想它看起来像这样:

what the graph looks like

最后,我尝试使用freeGraph(图形)释放图形。

void freeEdge(edgePointer e){
    if (e != NULL) {
        freeEdge(e->next);
        freeGraph(e->connectsTo);
        free(e);
        e = NULL;
    }
}

void freeGraph(vertexPointer v){
    if (v != NULL) {
        freeEdge(v->next);
        free(v);
        v = NULL;
    }
}

这就是valgrind开始抱怨“无效读取大小4”,“地址0x41fb0d4是一个大小为8的块中的4个字节”和“无效的free()”。它还说它有8个mallocs和9个释放。

我认为问题是节点D的内存已经被释放,然后我试图再次释放它。但是,如果不改变数据结构,我认为没有办法做到这一点。

什么是防止这些错误并正确释放图表的最佳方法,如果可能的话,无需更改数据结构?此外,如果此代码有任何其他问题,请告诉我们。谢谢!

映入眼帘, 分号

5 个答案:

答案 0 :(得分:2)

缺乏对所有引用的了解使得这有点困难。有点像黑客,但面对同样的问题,我可能会使用一个指针集(一个唯一值列表,在这种情况下是指针)。

  • 遍历整个图形,只有当节点指针推入集合时才会 尚未出现(这是' set'的定义)
  • 走完集合,释放每个指针(因为它们是唯一的,没有问题 一个双免费的)
  • graph设置为NULL。

我确信这有一个优雅的递归解决方案,但面对所述的任务,这似乎可行而且不是过于复杂。

答案 1 :(得分:2)

您可以将它们分配到内存池中,而不是在全局堆上分配节点和边缘。要释放图表,请释放整个池。

答案 2 :(得分:1)

我会通过设计一种方法来解决这个问题,首先从图中干净地删除每个节点,然后再释放它。要干净地执行此操作,您必须弄清楚哪些其他节点正在引用您要删除的节点并删除这些边缘。一旦边缘被移除,如果您碰巧到达之前引用已删除节点的另一个节点,则边缘已经消失,您将无法再次尝试删除它。

最简单的方法是修改数据结构以保存对“传入”边缘的引用。这样你可以做类似的事情:

v->incoming[i]->next = null; // do this for each edge in incoming
freeEdge(v->next);
free(v);
v = NULL;

如果您不想更新数据结构,则会遇到一个难以解决的问题:在图表中搜索与您要删除的节点有边缘的节点。

答案 3 :(得分:0)

这是因为你在这里进行了两次递归,他们互相踩踏。 freeGraph被调用一次以释放D(例如,来自B),然后当freeGraph的初始调用从freeEdge返回时,您尝试释放v - 这是已经处理得更深了。没有插图,这是一个糟糕的解释,但是你去了。

你可以摆脱一次递归,这样他们就不会“越过”,或者你可以在每次自由之前检查一下这个节点是否已被递归的另一个分支处理过。

答案 4 :(得分:0)

是的,问题是D可以通过两条路径到达并释放两次。

您可以分两个阶段完成: 阶段1:将您到达的节点插入“集”数据结构。 阶段2:释放“set”数据结构中的节点。

该集数据结构的可能实现,需要扩展您的数据结构: 使用布尔标志标记数据结构中的所有节点,因此不要将它们插入两次。 对另一个所有节点的链表使用另一个“next”指针。一个简单的。

另一种实现,没有扩展您的数据结构:像C ++ std :: set<>

这样的东西

另一个问题:当你从A开始时,你确定可以到达所有节点吗? 要避免此问题,请在创建时将所有节点插入“set”数据结构(此时不需要标记)。