簡體   English   中英

如何使用嵌套的雙指針釋放結構指針?

[英]How do I free struct pointers with nested double pointers?

我在底部粘貼了分配大量指針但沒有釋放任何指針的代碼。 我有一個名為Node的結構,它的字段類型為struct Node** 在我的主要 function 我有變量: Node** nodes = malloc(size * typeof(Node*)); . 我想知道如何正確釋放nodes

typedef struct Node {
    size_t id;              // identifier of the node
    int data;               // actual data
    size_t num_parents;     // actual number of parent nodes
    size_t size_parents;    // current maximum capacity of array of parent nodes
    struct Node** parents;  // all nodes that connect from "upstream"
    size_t num_children;    // actual number of child nodes
    size_t size_children;   // current maximum capacity of array of children nodes
    struct Node** children; // all nodes that connect "downstream"
} Node;

我已經將整個代碼粘貼在底部,因為它已經幾乎是最小的了(這里我們不需要的只是打印 function 和find_smallest_value函數)。 VS2019 還為我分配每個節點的主 function 的主循環中的兩行提供了兩個警告:

Node** nodes = malloc((num_nodes + 1) * sizeof(Node*));
        for (size_t i = 1; i <= num_nodes; i++) {
            nodes[i] = malloc(sizeof(Node)); // WARNING Buffer overrun while writing to 'nodes':  the writable size is '((num_nodes+1))*sizeof(Node *)' bytes, but '16' bytes might be written.
            nodes[i]->id = i; // WARNING Reading invalid data from 'nodes':  the readable size is '((num_nodes+1))*sizeof(Node *)' bytes, but '16' bytes may be read.

我根本不明白這些警告。 最后,您可以從該網站獲得該程序的大量輸入。 只需將其保存到文本文件並修改主 function 中的硬編碼文件名即可。 如果我注釋掉我嘗試釋放節點的最后幾行,程序運行良好。 我嘗試解除分配會使程序崩潰。 如果有人能解釋正確的方法,我將不勝感激。

解釋代碼的目的:

底部的代碼具有以下目標。 我正在嘗試構建一個有向圖,其中每個頂點都有一個 label 和一個值。 這種圖表的一個例子。 我感興趣的圖表都代表層次結構。 我要對這些圖執行兩個操作: I. 給定一個頂點,在層次結構中找到它上面的最小值並打印它的值; 二、 給定一對頂點,交換它們的位置。 例如,給定圖中的頂點 4 和 2,操作 II 的結果將是相同的圖,但標記為 2 和 4 的頂點將交換它們的標簽和數據。 給定頂點 6,操作 I 的結果將是“18”。 我相信我成功地實施了這兩個操作。

我的主要 function 從txt文件中讀取以構建數據結構,我選擇將其作為多重鏈表。 任何輸入文件都應具有以下格式(該文件生成如圖所示的圖形並對其執行一些操作):

7 8 9
21 33 33 18 42 22 26
1 2
1 3
2 5
3 5
3 6
4 6
4 7
6 7
P 7
T 4 2
P 7
P 5
T 1 4
P 7
T 4 7
P 2
P 6
  • 第一行包含三個數字:頂點數(節點)、邊數( k ,連接)和指令數( l ,操作 I 或 II)。
  • 第二行是每個節點中的數據。 標簽對應於節點的索引。
  • 接下來的k行由兩個節點標簽組成:左邊是父節點,右邊是子節點。
  • 接下來的l行由指令組成。 P代表操作I,后面是節點的label。 T代表操作II,后面是要交換的節點的兩個標簽。
  • 整個圖案可以重復。

編碼:

#include<stdlib.h>
#include<stdio.h>

typedef unsigned int uint;

typedef struct Node {
    size_t id;              // identifier of the node
    int data;               // actual data
    size_t num_parents;     // actual number of parent nodes
    size_t size_parents;    // current maximum capacity of array of parent nodes
    struct Node** parents;  // all nodes that connect from "upstream"
    size_t num_children;    // actual number of child nodes
    size_t size_children;   // current maximum capacity of array of children nodes
    struct Node** children; // all nodes that connect "downstream"
} Node;

Node** reallocate_node_array(Node** array, size_t* size) {
    Node** new_array = realloc(array, sizeof(Node*) * (*size) * 2);
    if (new_array == NULL) {
        perror("realloc");
        exit(1);
    }
    *size *= 2;
    return new_array;
}

// The intention is to pass `num_children` or `num_parents` as `size` in order to decrease them
void remove_node(Node** array, size_t* size, size_t index) {
    for (size_t i = index; i < *size - 1; i++) {
        array[i] = array[i + 1];
    }
    (*size)--; // the decrement to either `num_children` or `num_parents`
}

void remove_parent(Node* node, size_t id) {
    for (size_t i = 0; i < node->num_parents; i++) {
        if (node->parents[i]->id == id) {
            remove_node(node->parents, &node->num_parents, i);
        }
    }
}

void remove_child(Node* node, size_t id) {
    for (size_t i = 0; i < node->num_children; i++) {
        if (node->children[i]->id == id) {
            remove_node(node->children, &node->num_children, i);
        }
    }
}

void add_parent(Node* node, Node* parent) {
    if (node->num_parents >= node->size_parents) {
        node->parents = reallocate_node_array(node->parents, &node->size_parents);
    }
    node->parents[node->num_parents++] = parent;
}

void add_child(Node* node, Node* child) {
    if (node->num_children >= node->size_children) {
        node->children = reallocate_node_array(node->children, &node->size_children);
    }
    node->children[node->num_children++] = child;
}

uint number_of_digits(int n) {
    uint d = 0;
    do { d++; n /= 10; } while (n != 0);
    return d;
}
// return format: "{ parent1.id parent2.id ...} { id data } { child1.id child2.id ...}"
void print_node(Node node) {
    printf("{ ");
    for (size_t i = 0; i < node.num_parents; i++) {
        printf("%zu ", node.parents[i]->id);
    }
    printf("} [ %zu %d ] { ", node.id, node.data);
    for (size_t i = 0; i < node.num_children; i++) {
        printf("%zu ", node.children[i]->id);
    }
    printf("}\n");
}

void switch_nodes(Node* n1, Node* n2, Node** array) {
    uint temp_id = n1->id;
    uint temp_data = n1->data;
    n1->id = n2->id;
    n1->data = n2->data;
    n2->id = temp_id;
    n2->data = temp_data;
    Node* temp = array[n1->id];
    array[n1->id] = array[n2->id];
    array[n2->id] = temp;
}

int find_smallest_valued_parent(Node* node, uint depth) {
    // has no parents
    if (node->num_parents == 0 || node->parents == NULL) {
        if (depth == 0) return -1; // there was no parent on first call (nothing to report)
        else return node->data;
    }
    else {
        depth++;
        int minimum_value = node->parents[0]->data; // we're guaranteed 1 parent
        for (size_t i = 0; i < node->num_parents; i++) {
            int next_value = find_smallest_valued_parent(node->parents[i], depth);
            if (node->parents[i]->data < next_value) next_value = node->parents[i]->data;
            if (next_value < minimum_value) minimum_value = next_value;
        }
        return minimum_value;
    }
}

void free_node_array(Node** array, size_t start, size_t end) {
    for (size_t i = start; i < end; i++) {
        free(array[i]);
    }
    free(array);
}

int main() {
    char* file_name = "input_feodorv.txt";

    FILE* data_file = fopen(file_name, "r");
    if (data_file == NULL) {
        printf("Error: invalid file %s", file_name);
        return 1;
    }
    for (;;) {
        size_t num_nodes, num_relationships, num_instructions;
        if (fscanf(data_file, "%zu %zu %zu\n", &num_nodes, &num_relationships, &num_instructions) == EOF)
            break;

        Node** nodes = malloc((num_nodes + 1) * sizeof(Node*));
        for (size_t i = 1; i <= num_nodes; i++) {
            nodes[i] = malloc(sizeof(Node)); // WARNING Buffer overrun while writing to 'nodes':  the writable size is '((num_nodes+1))*sizeof(Node *)' bytes, but '16' bytes might be written.
            nodes[i]->id = i; // WARNING Reading invalid data from 'nodes':  the readable size is '((num_nodes+1))*sizeof(Node *)' bytes, but '16' bytes may be read.
            fscanf(data_file, "%u ", &nodes[i]->data);
            nodes[i]->num_children = 0;
            nodes[i]->size_children = 2;
            nodes[i]->children = (Node**)malloc(2 * sizeof(Node*));
            for (size_t j = 0; j < 2; j++) nodes[i]->children[j] = malloc(sizeof(Node));
            nodes[i]->num_parents = 0;
            nodes[i]->size_parents = 2;
            nodes[i]->parents = (Node**)malloc(2 * sizeof(Node*));
            for (size_t j = 0; j < 2; j++) nodes[i]->parents[j] = malloc(sizeof(Node));
        }

        for (size_t i = 0; i < num_relationships; i++) {
            size_t parent_id, child_id;
            fscanf(data_file, "%zu %zu\n", &parent_id, &child_id);

            add_child(nodes[parent_id], nodes[child_id]);
            add_parent(nodes[child_id], nodes[parent_id]);
        }

        for (size_t i = 0; i < num_instructions; i++) {
            char instruction;
            fscanf(data_file, "%c ", &instruction);
            if (instruction == 'P') {
                size_t id;
                fscanf(data_file, "%zu\n", &id);
                int minimum_value = find_smallest_valued_parent(nodes[id], 0);
                if (minimum_value == -1) printf("*\n");
                else printf("%u\n", minimum_value);
            }
            else {
                size_t n1_id, n2_id;
                fscanf(data_file, "%zu %zu\n", &n1_id, &n2_id);
                switch_nodes(nodes[n1_id], nodes[n2_id], nodes);
            }
        }
        /**/
        for (size_t i = 1; i <= num_nodes; i++) {
            free_node_array(nodes[i]->parents, 0, nodes[i]->size_parents);
            free_node_array(nodes[i]->children, 0, nodes[i]->size_children);
        }
        free_node_array(nodes, 0, num_nodes);
        /**/
    }
}

您的代碼中存在 memory 泄漏。 main() function 中,您正在執行以下操作:

    nodes[i]->children = (Node**)malloc(2 * sizeof(Node*));
    for (size_t j = 0; j < 2; j++) nodes[i]->children[j] = malloc(sizeof(Node));

    nodes[i]->parents = (Node**)malloc(2 * sizeof(Node*));
    for (size_t j = 0; j < 2; j++) nodes[i]->parents[j] = malloc(sizeof(Node));

這意味着,將 memory 分配給nodes[i]->children[j]nodes[i]->parents[j]指針。

add_child()add_parent() function 中,您使它們指向其他節點,從而導致丟失分配的 memory 參考:

void add_parent(Node* node, Node* parent) {
    .....
    node->parents[node->num_parents++] = parent;
}

void add_child(Node* node, Node* child) {
    .....
    node->children[node->num_children++] = child;
}

您實際上不需要在main()中將 memory 分配給nodes[i]->children[j]nodes[i]->parents[j]指針,因為這些指針假定指向圖形的現有節點並且您已經在main()中將 memory 分配給這些節點:

nodes[i] = malloc(sizeof(Node));

nodes[i]是給定圖的所有節點的數組元素, childrens節點和parents節點指針應僅指向這些節點。

現在來釋放這些指針:

您釋放圖形節點的方式不正確。 查看free_node_array() function:

void free_node_array(Node** array, size_t start, size_t end) {
    for (size_t i = start; i < end; i++) {
        free(array[i]);
    }
    free(array);
}

你這樣稱呼它:

        for (size_t i = 1; i <= num_nodes; i++) {
            free_node_array(nodes[i]->parents, 0, nodes[i]->size_parents);
            free_node_array(nodes[i]->children, 0, nodes[i]->size_children);
        }

這意味着,您正在釋放指針數組nodes[i]->parentsnodes[i]->children指向的指針。 nodes[i]->parentsnodes[i]->children的成員是指向nodes數組元素的指針。 一個節點完全有可能是1 1多個父節點的子節點,並且父節點可以有多個子節點。 現在假設一個子節點被2父節點指向的情況,比如n1n2 當您調用free_node_array() function 並傳遞第一個父節點 ( n1 ) 時,它將結束您釋放該子節點,並且當free_node_array() function 以釋放第二個父節點 ( n2 ) 時,它將嘗試釋放節點在釋放n1時已經釋放。

所以,這種釋放 memory 的方式是不正確的。 釋放parents的正確方法是簡單地釋放nodes數組的元素,因為它是包含給定圖形的所有節點的數組,並且children指針應該只指向這些節點。 無需遍歷父節點和子節點的層次結構。 要適當地釋放圖形,您應該執行以下操作:

  • 遍歷nodes數組和數組的每個元素:

    • 釋放parents指針數組( free (nodes[i]->parents )。
    • 釋放children指針數組( free (nodes[i]->children )。
    • 釋放nodes數組的那個元素( free (nodes[i] )。

一旦完成,然后釋放nodes數組 - free (nodes)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM