[英]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]->parents
和nodes[i]->children
指向的指針。 nodes[i]->parents
和nodes[i]->children
的成員是指向nodes
數組元素的指針。 一個節點完全有可能是1
1
多個父節點的子節點,並且父節點可以有多個子節點。 現在假設一個子節點被2
父節點指向的情況,比如n1
和n2
。 當您調用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.