【发布时间】:2022-01-12 22:04:43
【问题描述】:
我正在研究一个以图为主题的挑战问题,因此我决定实现一个多重链表(这种数据结构可以表示有向图)。当我尝试为列表创建节点时遇到问题。该程序编译得很好,但是当它运行时,它只会到达某个点并且没有警告就退出。在 VS2019 中以调试模式运行它,IDE 显示我正在尝试取消引用空指针。事实上,在它编译之前,它就强调了可疑的行并警告这可能会发生。但我完全不明白为什么。这是链表的实现(用最小的工作示例,并且确实意味着 minimal,我已尽力...):
#include<stdlib.h>
#include<stdio.h>
typedef unsigned int uint;
typedef struct Node {
uint id;
uint data;
size_t num_parents;
size_t size_parents;
struct Node * parents;
size_t num_children;
size_t size_children;
struct Node * children;
} Node;
/*/ ORIGINAL PROBLEMATIC REALLOCATING FUNCTION
Node * reallocate_node_array(Node * array, size_t* size) {
Node * new_array = new_array(Node, *size * 2); // this doesn't seem to be working as I expected
for (size_t i = 0; i < *size; i++) {
new_array[i] = array[i]; // FAULTY LINE
}
*size *= 2;
return new_array;
}
/**/
//NEW VERSION EDITED TO REFLECT CRAIG ESTEY'S COMMENTS AND ANSWER
Node * reallocate_node_array(Node * array, size_t* size) {
array = realloc(array, (*size) * 2);
if (array == NULL) {
perror("realloc");
exit(1);
}
*size *= 2;
return array;
}
void remove_node(Node * array, size_t * size, size_t index) {
for (int i = index; i < *size - 1; i++) {
array[i] = array[i + 1];
}
(*size)--;
}
void remove_parent (Node * node, uint id) {
for (int 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, uint id) {
for (int i = 0; i < node->num_children; i++) {
if (node->children[i].id == id) {
remove_node(node->children, &node->num_children, i);
}
}
}
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;
}
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;
}
int main() {
char * file_name = "input.txt";
FILE * data_file = fopen(file_name, "r");
if (data_file == NULL) {
printf("Error: invalid file %s", file_name);
return 1;
}
uint num_nodes, num_relationships;
fscanf(data_file, "%u %u\n", &num_nodes, &num_relationships);
// I'm sorry that I'm not checking for the result of malloc in this block.
// I promise I'll be more responsible in the future.
Node * nodes = (Node*)malloc((num_nodes + 1) * sizeof(Node));
for (size_t i = 1; i <= num_nodes; i++) {
nodes[i].id = i;
fscanf(data_file, "%u ", &nodes[i].data);
nodes[i].num_children = 0;
nodes[i].size_children = 10;
nodes[i].children = (Node*)malloc(10 * sizeof(Node)); // FAULTY LINE #1
nodes[i].num_parents = 0;
nodes[i].size_parents = 10;
nodes[i].parents = (Node*)malloc(10 * sizeof(Node)); // FAULTY LINE #2
}
for (uint i = 0; i < num_relationships; i++) {
uint parent_id, child_id;
fscanf(data_file, "%u %u\n", &parent_id, &child_id);
add_child(&employees[parent_id], &employees[child_id]);
add_parent(&employees[child_id], &employees[parent_id]);
}
return 0;
}
当它显示“FAULTY LINE #1”和“#2”时,调试器告诉我程序已到达断点(引发异常)。
main函数的重点是构建如下结构(图):
A directed graph with small number of nodes。最简洁的方法是从文件中读取指令。这里是input.txt的内容:
7 8
21 33 33 18 42 22 26
1 2
1 3
2 5
3 5
3 6
4 6
4 7
6 7
第一行:7是节点数; 8 是连接数(关系)。
所有其他行:左数为父节点;正确的数字是子节点。
所以,我的问题是,我无法通过 reallocate_node_array 函数以及后来的“FAULTY LINE #1”和“#2”。
编辑
所以我在上面进行了很多编辑,以提供一个最低限度的工作示例并进一步阐明我的背景和困难。无论我做错了什么,如果你能告诉我,我将不胜感激。
然而,在我根据 Craig Estey 的批评编辑了我的 reallocate_node_array 函数之后,我能够在调试中更进一步,并在上述实现中发现了一些可怕的错误。最重要的是我的结构Node 的字段parents 和children 需要是Node** 类型而不是Node*,因为它们应该是数组以表示乘法-链表。考虑到这一点,我重写了如下实现,它按预期运行。但是,我遇到了使用此代码执行进一步任务的问题,这些问题不在此问题的范围内。如果我要提出一个新问题,我一定会牢记您的所有批评,下次尝试写一个好问题。
感谢大家的所有反馈。
#include<stdlib.h>
#include<stdio.h>
typedef unsigned int uint;
typedef struct Node {
uint 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;
void reallocate_node_array(Node** array, size_t* size) {
array = realloc(array, sizeof(Node*) * (*size) * 2);
if (array == NULL) {
perror("realloc");
exit(1);
}
*size *= 2;
}
// 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, uint 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, uint 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) {
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) {
reallocate_node_array(node->children, &node->size_children);
}
node->children[node->num_children++] = child;
}
int main() {
char* file_name = "input.txt";
FILE* data_file = fopen(file_name, "r");
if (data_file == NULL) {
printf("Error: invalid file %s", file_name);
return 1;
}
uint num_nodes, num_relationships;
fscanf(data_file, "%u %u\n", &num_nodes, &num_relationships);
Node* nodes = (Node*)malloc((num_nodes + 1) * sizeof(Node));
for (size_t i = 1; i <= num_nodes; i++) {
nodes[i].id = i;
fscanf(data_file, "%u ", &nodes[i].data);
nodes[i].num_children = 0;
nodes[i].size_children = 10;
nodes[i].children = (Node**)malloc(10 * sizeof(Node*));
for (size_t j = 0; j < 10; j++) nodes[i].children[j] = (Node*)malloc(sizeof(Node));
nodes[i].num_parents = 0;
nodes[i].size_parents = 10;
nodes[i].parents = (Node**)malloc(10 * sizeof(Node*));
for (size_t j = 0; j < 10; j++) nodes[i].parents[j] = (Node*)malloc(sizeof(Node));
}
for (uint i = 0; i < num_relationships; i++) {
uint parent_id, child_id;
fscanf(data_file, "%u %u\n", &parent_id, &child_id);
add_child(&nodes[parent_id], &nodes[child_id]);
add_parent(&nodes[child_id], &nodes[parent_id]);
}
return 0;
}
【问题讨论】:
-
我使用这个定义作为简写:
#define new_array(type, size) type*)malloc(size*sizeof(type))摆脱它。然后弄清楚为什么事情会随着它到位...... -
首先,检查
malloc是否返回NULL。那么*size在分配时的值是多少呢? -
对
reallocate_node_array的调用在哪里?请编辑您的问题并发布。如果是(例如):myarray = reallocate_node_array(myarray,&myarray_size),那么myarray的 original 值会泄露(因为函数不会 notfree旧/原始数组指针) .除非您尝试创建单独的 duplicate 副本,否则为什么不直接使用realloc? -
我按照@AndrewHenle 的建议摆脱了#define,并且遇到了一个可能与问题无关的不同错误。我正在调查。
-
@CraigEstey
realloc可能是最好的方法。我来自 C++ 世界,在 C 方面不是很有经验,所以我正在尝试练习,这就是我这样做的原因。我不知道realloc有不同的效果。对reallocate_node_array的调用是这样的:node->children = reallocate_node_array(node->children, &node->size_children);
标签: c struct linked-list malloc doubly-linked-list