追踪内存泄漏,释放不正确?

Tracking down memory leak, free incorrectly?

下午,我收到一个奇怪的 valgrind 错误报告。请参阅所附图片。 我有一个释放堆的函数,或者我虽然做了。需要帮助追踪这个错误。欢迎修复,但也欢迎解释。

我对 valgrind 的分析跟踪到第 67 行,当我创建节点时,所以我知道我很可能忘记用我的 free 函数释放它。

#include "stdio.h"
#include "stdlib.h"
#include "limits.h"

struct adj_list_node
{
    int vertex;
    int weight;
    struct adj_list_node *next;
};

struct adj_list
{
    struct adj_list_node *head;
};

struct graph
{
    int vertices;
    struct adj_list *array;
};

struct adj_list_node* create_node(int vertex, int weight);
struct graph *create_graph(int vertices);
void add_edge(struct graph *graph, int src_vertex, int dst_vertex, int weight);
void free_nodes(struct adj_list_node *node);
void free_graph(struct graph *graph);

struct min_heap_node
{
    int vertex;
    int distance;
};

struct min_heap
{
    int size;
    int capacity;
    int *position;
    struct min_heap_node **array;
};

struct min_heap_node *create_heap_node(int vertex, int distance);
struct min_heap *create_heap(int capacity);

void swap_node(struct min_heap_node** a, struct min_heap_node** b);
int is_empty(struct min_heap* heap);
void min_heapify(struct min_heap *heap, int parent);
struct min_heap_node *extract_min_node(struct min_heap* heap);
int is_in_min_heap(struct min_heap *heap, int index);

void decrease_key(struct min_heap *heap, int index, int distance);
void dijkstra(struct graph* graph, int src_vertex);
void delete_heap(struct min_heap *heap);
void print_path(int parent[], int vertex);
void print_array(int dist[], int n,int parent[]);

#include "dikjkstra.h"

struct adj_list_node* create_adj_node(int vertex, int weight)
{
    struct adj_list_node *new_node = malloc(sizeof(struct adj_list_node));
    new_node->vertex = vertex;
    new_node->weight = weight;
    new_node->next = NULL;
    return new_node;
}

struct graph *create_graph(int vertices)
{
    struct graph *new_graph = malloc(sizeof(struct graph));
    new_graph->vertices = vertices;
    new_graph->array = malloc(sizeof(struct adj_list) * vertices);

    for (int i = 0;i < vertices; ++i) {
        new_graph->array[i].head = NULL;
    }

    return new_graph;
}

void add_edge(struct graph *graph, int src_vertex, int dst_vertex, int weight)
{
    struct adj_list_node *new_node = create_adj_node(dst_vertex, weight);
    new_node->next = graph->array[src_vertex].head;
    graph->array[src_vertex].head = new_node;

    new_node = create_adj_node(src_vertex, weight);
    new_node->next = graph->array[dst_vertex].head;
    graph->array[dst_vertex].head = new_node;
}

void free_nodes(struct adj_list_node *node)
{
    if (node == NULL) {
        return;
    }

    struct adj_list_node *temp_node = node;

    while (temp_node) {
        struct adj_list_node *removed_node = temp_node;
        temp_node = removed_node->next;
        removed_node->next = NULL;
        free(removed_node);
    }
}

void free_graph(struct graph *graph)
{
    if(graph->array == NULL) {
        return;
    }

    for (int i = 0; i < graph->vertices; ++i) {
        free_nodes(graph->array[i].head);
    }
    free(graph->array);
}


struct min_heap_node *create_heap_node(int vertex, int distance)
{
    struct min_heap_node *new_node = malloc(sizeof(struct min_heap_node));
    new_node->vertex = vertex;
    new_node->distance = distance;
    return new_node;
}

struct min_heap *create_heap(int capacity)
{
    struct min_heap *new_heap = malloc(sizeof(struct min_heap));
    new_heap->size = 0;
    new_heap->capacity = capacity;
    new_heap->position = malloc(capacity * sizeof(int));
    new_heap->array = malloc(sizeof(struct min_heap_node *) * capacity);
    return new_heap;
}

void swap_node(struct min_heap_node** a, struct min_heap_node** b)
{
    struct min_heap_node *temp_node;
    temp_node = *a;
    *a = *b;
    *b = temp_node;
}

int is_empty(struct min_heap* heap)
{
    return heap->size == 0;
}

void min_heapify(struct min_heap *heap, int parent)
{
    int left_child = 2 * parent + 1;
    int right_child = 2 * parent + 2;

    int smallest = parent;

    if (left_child < heap->size && heap->array[left_child]->distance < heap->array[smallest]->distance) {
        smallest = left_child;
    }

    if ( right_child < heap->size && heap->array[right_child]->distance < heap->array[smallest]->distance) {
        smallest = right_child;
    }

    if (smallest != parent) {
        struct min_heap_node *smallest_node = heap->array[smallest];
        struct min_heap_node *temp_node = heap->array[parent];

        heap->position[smallest_node->vertex] = parent;
        heap->position[temp_node->vertex] = smallest;

        swap_node(&heap->array[smallest], &heap->array[parent]);
        min_heapify(heap, smallest);
    }
}

struct min_heap_node *extract_min_node(struct min_heap *heap)
{
    if (is_empty(heap)) {
        return NULL;
    }

    struct min_heap_node *root_node = heap->array[0];

    struct min_heap_node *last_node = heap->array[heap->size - 1];
    heap->array[0] = last_node;

    heap->position[root_node->vertex] = heap->size - 1;
    heap->position[last_node->vertex] = 0;

    --(heap)->size;
    min_heapify(heap, 0);

    return root_node;
}

int is_in_min_heap(struct min_heap *heap, int index)
{
    if (heap->position[index] < heap->size) {
        return 1;
    } else {
        return 0;
    }
}

void delete_heap(struct min_heap *heap)
{
    free(heap->array);
    free(heap->position);
    free(heap);
}

void decrease_key(struct min_heap *heap, int index, int distance)
{
    int key = heap->position[index];

    heap->array[key]->distance = distance;

    while(key && heap->array[key]->distance < heap->array[(key - 1) / 2]->distance) {
        heap->position[heap->array[key]->vertex] = (key - 1) / 2;
        heap->position[heap->array[(key - 1) / 2]->vertex] = key;
        swap_node(&heap->array[key], &heap->array[(key - 1) / 2]);

        key = (key - 1) / 2;
    }
}

void dijkstra(struct graph* graph, int src_vertex)
{
    int vertices = graph->vertices;
    struct min_heap *heap = create_heap(vertices);
    int distance[vertices];
    heap->size = vertices;
    int parent[vertices];
    parent[src_vertex] = src_vertex;

    for(int i = 0; i < vertices; ++i) {
        distance[i] = INT_MAX;
        heap->array[i] = create_heap_node(i, INT_MAX);
        heap->position[i] = i;
    }

    distance[src_vertex] = 0;
    decrease_key(heap, src_vertex, distance[src_vertex]);

    while(!is_empty(heap)) {
        struct min_heap_node *min_node = extract_min_node(heap);
        int u = min_node->vertex;

        for (struct adj_list_node *cursor = graph->array[u].head; cursor; cursor = cursor->next) {
            int v = cursor->vertex;

            if(is_in_min_heap(heap,v) && distance[u]!=INT_MAX && distance[u] + cursor->weight < distance[v]) {
                distance[v] = cursor->weight + distance[u];
                parent[v] = u;
                decrease_key(heap, v, distance[v]);
            }
        }
    }
    print_array(distance, vertices, parent);
    delete_heap(heap);
}

void print_path(int parent[], int vertex)
{
    if (parent[vertex]==vertex) {
        return;
    }
    print_path(parent, parent[vertex]);
    printf("%d->", parent[vertex]);
}

void print_array(int cost[], int vertices,int parent[])
{
    printf("\nvertex\t\tcost\t\tpath\n");
    for(int i = 0; i < vertices ; ++i) {
        printf("%d\t\t\t%d\t\t\t", i, cost[i]);
        print_path(parent, i);
        printf("%d\n", i);
    }
}

/**
 *            6
 *     0-------------1
 *     |            /|\
 *     |           / | \
 *     |          /  |  
 *     |         /   |   \
 *     |        /    |    \
 *     |       /     |     \
 *    1|     2/     2|      2
 *     |     /       |     /
 *     |    /        |    /
 *     |   /         |   /5
 *     |  /          |  /
 *     | /           | /
 *     |/            |/
 *     3-------------4
 *            1
 */

int main()
{
    int total_vertex = 5;

    struct graph* graph = create_graph(total_vertex);

    add_edge(graph, 0, 1, 6);
    add_edge(graph, 0, 3, 1);

    add_edge(graph, 1, 0, 6);
    add_edge(graph, 1, 3, 2);
    add_edge(graph, 1, 4, 2);
    add_edge(graph, 1, 2, 5);

    add_edge(graph, 2, 1, 5);
    add_edge(graph, 2, 4, 5);

    add_edge(graph, 3, 0, 1);
    add_edge(graph, 3, 1, 2);
    add_edge(graph, 3, 4, 1);

    add_edge(graph, 4, 3, 1);
    add_edge(graph, 4, 1, 2);
    add_edge(graph, 4, 2, 5);

    dijkstra(graph,0);

    free_graph(graph);
    free(graph);
    return 0;
}

/**
* Output
* vertex    cost        path
* 0     0       0
* 1     3       0->3->1
* 2     7       0->3->4->2
* 3     1       0->3
* 4     2       0->3->4
*/

Valgrind 说泄漏的内存是由 create_heap_node 分配的,这意味着您没有释放一些堆节点。我们如何找到它?

每个malloc 都需要一个free。如果每个创建函数都有匹配的自由函数(并使用一致的命名方案),则更容易检查这一点。

  • create_heap_node
    • 缺失
  • create_heap
    • delete_heap(应该是free_heap)
  • create_adj_node
    • free_nodes(应该是free_adj_nodes)
  • create_graph
    • free_graph

乍一看,您缺少 free_heap_node。

void delete_heap(struct min_heap *heap)
{
    free(heap->array);  // potential memory leak
    free(heap->position);
    free(heap);
}

也许其他东西正在释放这些节点,但 delete_heap 不应该这样假设。可能存在内存泄漏。

dijkstra 是唯一使用 delete_heap 的函数。 dijkstra 在调用 delete heap 时假定堆为空,而事实确实如此。然而,dijkstra 提取节点但它从不释放它们。

    while(!is_empty(heap)) {
        struct min_heap_node *min_node = extract_min_node(heap);

        // min_node is removed from heap but never freed.
        // memory leak.
        // needs free_heap_node(min_node)
    }
    
    delete_heap(heap);

free_nodes 应该是 free_adj_nodes。您可以添加 free_adj_node 以获得完整性,这将使 free_nodes 更简单。

// free itself, return the next node
struct adj_list_node *free_adj_node(struct adj_list_node *node) {
    struct adj_list_node *next = node->next;
    free(node);
    return next;
}

void free_adj_nodes(struct adj_list_node *node)
{
    // No need to check if node is null.
    // If it is, the loop will simply not run.

    // Free nodes until the next node is null.
    while (node) {
        node = free_adj_node(node);
    }
}