如何使用嵌套双指针释放结构指针?
How do I free struct pointers with nested double pointers?
我在底部粘贴了分配大量指针但不释放任何指针的代码。我有一个名为 Node
的结构,它具有 struct Node**
类型的字段。在我的主要功能中,我有变量: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;
我将整个代码粘贴在底部,因为它已经几乎是最小的了(这里我们不需要的只是打印功能和 find_smallest_value
功能)。 VS2019 还针对我分配每个节点的主函数主循环中的两行给出了两个警告:
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.
我完全不理解这些警告。最后,您可以从 this website 获得此程序的大量输入。只需将其保存到文本文件并在主函数中修改硬编码文件名即可。如果我注释掉我尝试解除分配节点的最后几行,程序运行良好。我试图解除分配使程序崩溃。如果有人能解释正确的方法,我将不胜感激。
解释代码的用途:
底部的代码具有以下目标。我正在尝试构建一个有向图,其中每个顶点都有一个标签和一个值。 An example of such a graph. 我感兴趣的图表都表示层次结构。我要对这些图执行两个操作: I. 给定一个顶点,在层次结构中找到它上面具有最小值的那个并打印它的值;二。给定一对顶点,交换它们的位置。例如,给定该图中的顶点 4 和 2,操作 II 的结果将是相同的图,但标记为 2 和 4 的顶点将交换其标签和数据。给定顶点 6,操作 I 的结果将是“18”。我相信我成功地实施了这两个操作。
我的主要功能是从 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,后面是节点的标签。 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);
/**/
}
}
您的代码中存在内存泄漏。在 main()
函数中,您正在做:
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));
也就是说,分配内存给nodes[i]->children[j]
和nodes[i]->parents[j]
指针。
在 add_child()
和 add_parent()
函数中,您使它们指向某个其他节点,导致丢失分配的内存引用:
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()
中的 nodes[i]->children[j]
和 nodes[i]->parents[j]
指针分配内存,因为这些指针假定指向图形的现有节点,而您是已经在 main()
:
中为这些节点分配了内存
nodes[i] = malloc(sizeof(Node));
nodes[i]
是给定图的所有节点的数组元素,childrens
和 parents
指针应仅指向这些节点。
现在开始释放这些指针:
您释放图形节点的方式不正确。看free_node_array()
函数:
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()
函数并传递第一个父节点 (n1
) 时,它将结束您释放该子节点,而当调用 free_node_array()
函数以释放第二个父节点 (n2
), 它会在释放 n1
.
时尝试释放已经被释放的节点
所以,这种释放内存的方式是不正确的。释放内存的正确方法是简单地释放 nodes
数组的元素,因为该数组将包含给定图形的所有节点,并且 parents
和 children
指针应该仅指向这些节点。无需遍历父子节点的层次结构。要适当地释放图表,您应该这样做:
遍历nodes
数组,对于数组的每个元素:
- 释放
parents
指针数组 (free (nodes[i]->parents
)。
- 释放
children
指针数组 (free (nodes[i]->children
)。
- 释放
nodes
数组 (free (nodes[i]
) 的那个元素。
完成后释放 nodes
数组 - free (nodes)
.
我在底部粘贴了分配大量指针但不释放任何指针的代码。我有一个名为 Node
的结构,它具有 struct Node**
类型的字段。在我的主要功能中,我有变量: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;
我将整个代码粘贴在底部,因为它已经几乎是最小的了(这里我们不需要的只是打印功能和 find_smallest_value
功能)。 VS2019 还针对我分配每个节点的主函数主循环中的两行给出了两个警告:
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.
我完全不理解这些警告。最后,您可以从 this website 获得此程序的大量输入。只需将其保存到文本文件并在主函数中修改硬编码文件名即可。如果我注释掉我尝试解除分配节点的最后几行,程序运行良好。我试图解除分配使程序崩溃。如果有人能解释正确的方法,我将不胜感激。
解释代码的用途:
底部的代码具有以下目标。我正在尝试构建一个有向图,其中每个顶点都有一个标签和一个值。 An example of such a graph. 我感兴趣的图表都表示层次结构。我要对这些图执行两个操作: I. 给定一个顶点,在层次结构中找到它上面具有最小值的那个并打印它的值;二。给定一对顶点,交换它们的位置。例如,给定该图中的顶点 4 和 2,操作 II 的结果将是相同的图,但标记为 2 和 4 的顶点将交换其标签和数据。给定顶点 6,操作 I 的结果将是“18”。我相信我成功地实施了这两个操作。
我的主要功能是从 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,后面是节点的标签。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);
/**/
}
}
您的代码中存在内存泄漏。在 main()
函数中,您正在做:
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));
也就是说,分配内存给nodes[i]->children[j]
和nodes[i]->parents[j]
指针。
在 add_child()
和 add_parent()
函数中,您使它们指向某个其他节点,导致丢失分配的内存引用:
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()
中的 nodes[i]->children[j]
和 nodes[i]->parents[j]
指针分配内存,因为这些指针假定指向图形的现有节点,而您是已经在 main()
:
nodes[i] = malloc(sizeof(Node));
nodes[i]
是给定图的所有节点的数组元素,childrens
和 parents
指针应仅指向这些节点。
现在开始释放这些指针:
您释放图形节点的方式不正确。看free_node_array()
函数:
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()
函数并传递第一个父节点 (n1
) 时,它将结束您释放该子节点,而当调用 free_node_array()
函数以释放第二个父节点 (n2
), 它会在释放 n1
.
所以,这种释放内存的方式是不正确的。释放内存的正确方法是简单地释放 nodes
数组的元素,因为该数组将包含给定图形的所有节点,并且 parents
和 children
指针应该仅指向这些节点。无需遍历父子节点的层次结构。要适当地释放图表,您应该这样做:
遍历
nodes
数组,对于数组的每个元素:- 释放
parents
指针数组 (free (nodes[i]->parents
)。 - 释放
children
指针数组 (free (nodes[i]->children
)。 - 释放
nodes
数组 (free (nodes[i]
) 的那个元素。
- 释放
完成后释放 nodes
数组 - free (nodes)
.