【发布时间】:2021-12-27 00:34:05
【问题描述】:
我在底部粘贴了分配大量指针但不释放任何指针的代码。我有一个名为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 获得该程序的大量输入。只需将其保存到文本文件并在 main 函数中修改硬编码的文件名即可。如果我注释掉我尝试释放节点的最后几行,程序运行良好。我尝试解除分配会使程序崩溃。如果有人能解释正确的方法,我将不胜感激。
解释代码的用途:
底部的代码有以下目标。我正在尝试构建一个有向图,其中每个顶点都有一个标签和一个值。 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);
/**/
}
}
【问题讨论】:
-
array = new_array;不会修改调用者的变量。 -
不错的收获!非常小的变化,我想我可以运行代码完成! (我天真的算法太慢了,我等不到最后)。现在我只需要知道如何释放内存。
-
add_child和add_parent函数在这些语句node->parents[node->num_parents++] = parent;和node->children[node->num_children++] = child;中存在内存泄漏。请注意,每个节点parents[i]和children[i]指针都分配了内存,而在add_child和add_parent中,您正在使它们指向其他内存位置,从而丢失了分配内存的引用。 -
如果我错了,请纠正我:
node->children的类型为Node**。因此,当我执行node->children[i]时,我取消了指针的引用,然后通过执行node->children[i] = child,我将存储在child中的数据(一个地址)复制到存储地址的node->children[i]的数据中.地址node->children + i本身没有改变,只有其中存储的数据。我犯错了吗?如果是这样,那么正确的方法是什么?我希望 node->children[i] 中存储的地址与 child 中存储的地址相同。 -
nodes[i]->children[j]是一个指针。您正在为main()中的这些指针nodes[i]->children[j] = malloc(sizeof(Node));分配内存。nodes[i]->parents[j]指针也是如此。在add_child()和add_parent中,您使它们指向其他内存位置。
标签: c memory-management free realloc