如何释放包含动态分配的双指针到另一种结构类型的结构?

时间:2017-10-07 05:07:58

标签: c graph

我在C中创建了一个图形结构。下面给出了图形的结构定义。

typedef struct _GNODE_ {
    int nodeNumber;             // vertex/node number
    int adjNum;                 // number of adjacent nodes
    struct _GNODE_ **adjacent;  // array of pointers to adjacent nodes
} _GNODE_ ;

typedef struct _GRAPH_ {
    int vc;         // number of vertices
    char *name;     // graph name
    int **AM;       // adjacency matrix
    _GNODE_ **node; // array of pointer to each vertices
} _GRAPH_ ;

其中 _GNODE _ 是图表中节点的结构。

函数 readGraphFromTxt(char * fileName,_GRAPH_ * graph)读取包含图形邻接矩阵的文本文件,并调用另一个名为 createGraphFromAM(_GRAPH_ * graph)的函数创建图形结构。

int readGraphFromTxt(char *filename , _GRAPH_ *graph){
    FILE *fp;
    fp = fopen(filename, "r");
    int vc;
    int **AM;
    char *gname;
    if(fp != NULL){
        char graphName[100];
        fgets(graphName, 99, fp );
        gname = (char *)malloc(sizeof(char)*101);
        strncpy(gname , graphName, 99);
        fgets(graphName, 99, fp );
        fscanf(fp , "%d\n" , &vc);
        AM = (int **) malloc(sizeof(int*) * vc);
        for(int i=0; i<vc; i++){
            AM[i] = (int *)malloc(sizeof(int) * vc);
            if(fscanf(fp, "%s" , graphName) ==EOF)    break;
            for(int j=0; j<vc; j++){
                AM[i][j] = graphName[j] - '0';
            }
        }
    }
    if(AM != NULL) graph->AM = AM;
    if(gname != NULL )  graph->name = gname;
    graph->vc = vc;
    createGraphFromAM(graph);
    fclose(fp);
    return vc;
}

void createGraphFromAM(_GRAPH_ *graph) {
    graph->node = (_GNODE_ **)malloc(sizeof(_GNODE_) * graph->vc) ; // array of pointers to different nodes

    for (int i=0; i<graph->vc ; i++){
        graph->node[i] = (_GNODE_ *)malloc(sizeof(_GNODE_));
        graph->node[i]->adjNum=0;
        graph->node[i]->nodeNumber = i;     // node number
        graph->node[i]->adjacent = (_GNODE_ **)malloc(sizeof(_GNODE_)) ; // because allocating 0 byte is tricky
    }

    for (int i=0; i<graph->vc ; i++){
        for (int j=0; j<graph->vc ; j++){
            if(graph->AM[i][j]==1){     // check for adjacency between i and j
                graph->node[i]->adjNum++;   // if adjacent increment number of adjacent nodes number
                graph->node[i]->adjacent = (_GNODE_ **)realloc( graph->node[i]->adjacent , sizeof(_GNODE_) * (graph->node[i]->adjNum+1));   // reallocate the memory to hold new adjacent member
                graph->node[i]->adjacent[graph->node[i]->adjNum-1] = graph->node[j];    // points to another node
            }
        }
        graph->node[i]->adjacent[graph->node[i]->adjNum] = NULL; // set last adjacent node to NULL
    }
}

函数 freeGraph(_GRAPH_ * graph)应该解除分配给图形的所有内存。 但是在调用此函数时,程序会遇到SEGMENTATION FAULT

void freeGraph(_GRAPH_ *graph){
    // free graph
    for (int i=0; i<graph->vc; i++) {
        // free each node data
        printf("\nLoop: %d\n", i);
            // free each adjacent node
        for (int k=0; k<graph->node[i]->adjNum; k++){
            if(graph->node[i]->adjacent[k]!=NULL){
                free(graph->node[i]->adjacent[k]);
            }
        }
        if(graph->node[i]->adjacent!=NULL) {
            free(graph->node[i]->adjacent);
        }
        free(graph->node[i]);
    }
    free(graph->node);
    for(int i=0; i<graph->vc; i++)
        if(graph->AM[i]!=NULL) free(graph->AM[i]);
    free(graph->AM);
    free(graph->name);
    free(graph);
}

名为 printAllGraphNodeNumber(const _GRAPH_ * graph)的函数,用于打印所有节点编号及其相邻节点编号。

void printAllGraphNodeNumber(const _GRAPH_ *graph) {
    printf("NODES IN THE GRAPH: ");
    for (int i=0; i<graph->vc ; i++) {
        printf("\nNode Number : %d\n|----->  Adjacent Nodes: ",graph->node[i]->nodeNumber);
        for (int j=0; j<graph->node[i]->adjNum; j++){
            printf("%d , ", graph->node[i]->adjacent[j]->nodeNumber );
        }
    }
}

示例文件的内容&#34; Graph01.txt&#34;是: 最大顶点数为:20

one
AM
10
0100010011
1000110100
0000100001
0000000010
0110000100
1100000001
0000000001
0100100010
1001000101
1010011010

2 个答案:

答案 0 :(得分:1)

我通过删除函数 freeGraph()中注释为释放每个相邻节点的代码来解决此问题。结果代码如下:

void freeGraph(_GRAPH_ *graph){
    // free graph
    for (int i=0; i<graph->vc; i++) {
        // free each node data
        printf("\nLoop: %d\n", i);
            // free each adjacent node
            /*
        for (int k=0; k<graph->node[i]->adjNum; k++){
            if(graph->node[i]->adjacent[k]!=NULL){
                free(graph->node[i]->adjacent[k]);
            }
        }*/
        if(graph->node[i]->adjacent!=NULL) {
            free(graph->node[i]->adjacent);
        }
        free(graph->node[i]);
    }
    free(graph->node);
    for(int i=0; i<graph->vc; i++)
        if(graph->AM[i]!=NULL) free(graph->AM[i]);
    free(graph->AM);
    free(graph->name);
    // commenting this out : because this is not dynamically allocated 
    //    free(graph);
}

我这样做的原因是因为graph->node[i]->adjacent[k]是指向图的不同节点的指针,这些节点最后将被free(graph->node[i])释放。

我使用 Valgrind 进行了检查,效果非常好。显示健康清单的统计数据。所有分配都被释放。我想我很高兴。

PS:我仍然认为它不是最佳解决方案。但这就是我解决问题的方法。希望其他任何人都能提供更好的答案。

答案 1 :(得分:1)

这是对您的代码的改编。我已经清理了我在问题评论中提出的许多观点。特别是:

  • 从为实现保留的命名空间中删除结构名称。
  • 检查操作并报告错误。
  • 如果文件打开失败,则不会释放未初始化的指针。
  • 如果文件打开失败,代码不会尝试关闭未打开的文件流。
  • 由于分配失败退出程序,因此对空值的检查较少。
  • 代码计算一行中有多少个相邻节点,并一次分配正确的空间,而不是一次分配一个元素。
  • 我已经为邻接列表保留了null终止符,尽管它并不是必需的。
  • 默认情况下,程序从文件data中读取。您可以通过在命令行上指定要读取的文件来覆盖它。

我还修改了代码,以便readGraphFromTxt()返回一个已分配的指针,以便freeGraph()可以正确地释放它。

主要修复方法是确保每个malloc()被释放一次。在您诊断时,您试图释放邻接列表指向的节点,这是不正确的行为。这些节点应该被集体释放,因为它们是集体分配的。

我整理了印刷品(至少按照我的标准)。

我使用GitHub中提供的文件stderr.cstderr.h来简化错误报告。当错误报告很简单时,它不太可能被省略。我在几个地方作弊并使用assert()而不是正式的错误测试和消息。

代码

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stderr.h"

typedef struct Node
{
    int nodeNumber;
    int adjNum;
    struct Node **adjacent;
} Node;

typedef struct Graph
{
    int vc;
    char *name;
    int **AM;
    Node **node;
} Graph;

static void createGraphFromAM(Graph *graph);

static Graph *readGraphFromTxt(const char *filename)
{
    FILE *fp = fopen(filename, "r");
    if (fp == 0)
        err_syserr("failed to open file %s for reading\n", filename);
    Graph *graph = malloc(sizeof(*graph));
    if (graph == 0)
        err_syserr("failed to allocate %zu bytes\n", sizeof(*graph));
    char line[100];
    if (fgets(line, sizeof(line), fp) == 0)
        err_error("premature EOF - no graph name\n");
    line[strcspn(line, "\n")] = '\0';
    char *gname = strdup(line);
    if (fgets(line, sizeof(line), fp) == 0)
        err_error("premature EOF - no auxilliary info\n");
    int vc = -1;
    if (fscanf(fp, "%d", &vc) != 1)
        err_error("format error: didn't get an integer\n");
    if (vc < 1 || vc > 20)
        err_error("size of graph out of control: %d\n", vc);

    int **AM = (int **)malloc(sizeof(int *) * vc);
    if (AM == 0)
        err_syserr("failed to allocate %zu bytes\n", sizeof(int *) * vc);

    for (int i = 0; i < vc; i++)
    {
        AM[i] = (int *)malloc(sizeof(int) * vc);
        if (AM[i] == 0)
            err_syserr("failed to allocate %zu bytes\n", sizeof(int) * vc);
        if (fscanf(fp, "%s", line) == EOF)
            err_error("premature EOF - not enough lines of data for the adjacency matrix\n");
        if (strlen(line) < (size_t)vc)
            err_error("Adjacency matrix line is too short (got [%s] (%zu); wanted %d)\n",
                      line, strlen(line), vc);
        for (int j = 0; j < vc; j++)
        {
            assert(line[j] == '0' || line[j] == '1');
            AM[i][j] = line[j] - '0';
        }
    }

    graph->AM = AM;
    graph->name = gname;
    graph->vc = vc;
    graph->node = 0;
    createGraphFromAM(graph);
    fclose(fp);
    return graph;
}

/* How many times does val appear in array arr of size num? */
static inline size_t val_count(size_t num, const int arr[num], int val)
{
    assert(arr != 0);
    size_t count = 0;
    for (const int *end = arr + num; arr < end; arr++)
    {
        if (*arr == val)
            count++;
    }
    return count;
}

static void createGraphFromAM(Graph *graph)
{
    graph->node = (Node **)malloc(sizeof(*graph->node) * graph->vc);
    if (graph->node == 0)
        err_syserr("failed to allocate %zu bytes\n", sizeof(*graph->node) * graph->vc);

    for (int i = 0; i < graph->vc; i++)
    {
        graph->node[i] = (Node *)malloc(sizeof(Node));
        if (graph->node[i] == 0)
            err_syserr("failed to allocate %zu bytes\n", sizeof(Node));
        graph->node[i]->adjNum = val_count(graph->vc, graph->AM[i], 1);
        graph->node[i]->nodeNumber = i;
        size_t adj_size = sizeof(Node *) * (graph->node[i]->adjNum + 1);
        graph->node[i]->adjacent = (Node **)malloc(adj_size);
        if (graph->node[i]->adjacent == 0)
            err_syserr("failed to allocate %zu bytes\n", adj_size);
    }

    for (int i = 0; i < graph->vc; i++)
    {
        Node *node = graph->node[i];
        int adj = 0;
        for (int j = 0; j < graph->vc; j++)
        {
            if (graph->AM[i][j] == 1)
                node->adjacent[adj++] = graph->node[j];
        }
        node->adjacent[node->adjNum] = NULL;
    }
}

static void freeGraph(Graph *graph)
{
    for (int i = 0; i < graph->vc; i++)
    {
        free(graph->node[i]->adjacent);
        free(graph->node[i]);
    }
    free(graph->node);

    for (int i = 0; i < graph->vc; i++)
        free(graph->AM[i]);
    free(graph->AM);

    free(graph->name);
    free(graph);
}

static void printAllGraphNodeNumber(const Graph *graph)
{
    assert(graph != 0);
    printf("Nodes in the graph %s: %d\n", graph->name, graph->vc);
    for (int i = 0; i < graph->vc; i++)
    {
        printf("Node: %d - Adjacent Nodes: ", graph->node[i]->nodeNumber);
        const char *pad = "";
        for (int j = 0; j < graph->node[i]->adjNum; j++)
        {
            printf("%s%d", pad, graph->node[i]->adjacent[j]->nodeNumber);
            pad = ", ";
        }
        putchar('\n');
    }
}

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);
    const char *filename = "data";
    if (argc == 2)
        filename = argv[1];
    Graph *graph = readGraphFromTxt(filename);
    if (graph != 0)
    {
        printAllGraphNodeNumber(graph);
        freeGraph(graph);
    }
    return 0;
}

数据

问题中提供的内容的副本:

one
AM
10
0100010011
1000110100
0000100001
0000000010
0110000100
1100000001
0000000001
0100100010
1001000101
1010011010

输出

Nodes in the graph one: 10
Node: 0 - Adjacent Nodes: 1, 5, 8, 9
Node: 1 - Adjacent Nodes: 0, 4, 5, 7
Node: 2 - Adjacent Nodes: 4, 9
Node: 3 - Adjacent Nodes: 8
Node: 4 - Adjacent Nodes: 1, 2, 7
Node: 5 - Adjacent Nodes: 0, 1, 9
Node: 6 - Adjacent Nodes: 9
Node: 7 - Adjacent Nodes: 1, 4, 8
Node: 8 - Adjacent Nodes: 0, 3, 7, 9
Node: 9 - Adjacent Nodes: 0, 2, 5, 6, 8

升级到macOS High Sierra 10.13后,我(再一次)没有Valgrind。但是,其他一些工具让我放心,这可能是正确的。