C - 通过函数创建具有多个 children 的节点 - 分段错误
C - Creating node with multiple children by function - segmentation fault
我想创建具有多个 children 的树并创建每个节点都有一个函数。这是我的节点结构:
typedef struct node {
char *string; // Name of the node
int number_of_children;
struct node *children[];
} node;
这是我创建新节点的函数(有四个 children):
node *add_node(char *string, node *left, node *middle_left, node *middle_right, node *right) {
node *p;
if ((p = malloc(sizeof(node))) == NULL) yyerror("Memory error");
p->string = strdup(string);
p->children[p->number_of_children] = left;
p->children[p->number_of_children + 1] = middle_left;
p->children[p->number_of_children + 2] = middle_right;
p->children[p->number_of_children + 3] = right;
p->number_of_children = 4;
return p;
}
当我运行 p->string
下的这个函数值被更改为一些垃圾。当我想设置固定数量的 children(在我将 struct node *children[]
更改为 struct node *children[4]
的结构中)时,出现分段错误。你有什么想法?
你定义结构的方式,你必须事先知道你可以有多少children,因为你必须明确地为最后一个成员分配内存:
node *p = malloc(sizeof(node) + nchildren * sizeof(node));
您可以稍后重新分配以容纳更多节点,但这不是一种可行的方法,因为重新分配的内存的句柄可能会更改,这会破坏树连接。
有更好的方法让节点具有不同数量的 children,例如:
- 设定一个固定的最大 children 数,也许是 4 个,并使成员数组
children
明确地为 4 个元素长。保留一个数字,告诉您有多少 children 是有效的。
- 使 children 的列表成为动态分配的数组,以后可以重新分配。这意味着您有一个 two-level 分配:首先是正确的节点,然后是其 children.
的列表
- 保留 child 个节点的链表。想一想,您可以重新排列树,使每个节点都有一个
child
作为其最旧的 child 节点,并为每个节点的下一个最老的兄弟节点提供一个 sibling
节点。
我不确定你到底想做什么,但第一种方法似乎对你来说是最简单的。
您的代码还有其他几个错误:
您应该只在创建节点时分配内存。显然,您已经将有效的节点句柄传递给您的函数。这些节点已经分配了内存或者它们是NULL
。以下:
p = malloc(sizeof(*p));
p = pref;
将分配内存并立即失去该内存的唯一句柄,这是内存泄漏。这里不用分配,直接说p = pref
.
malloc
分配的内存未初始化;它包含垃圾。在使用之前初始化所有结构成员。或者,考虑使用 calloc
,它分配内存并将其清零。
if ((p = malloc(sizeof(node))) == NULL)
中的组合赋值和检查是有效的,但阅读起来很复杂。在我看来,最好将它们分为分配和后续检查。 (毕竟,它只是以一些额外的括号为代价节省了您两次输入 p
。)
您按数组传入可变数量的儿童的示例可能如下所示:
typedef struct node {
char *string;
size_t number_of_children;
struct node *children[4];
} node;
node *add_node(char *string, node *child[], size_t n)
{
node *p = malloc(sizeof(*p));
size_t i;
if (p == NULL) yyerror("Memory error");
p->string = strdup(string);
for (i = 0; i < n; i++) p->children[i] = child[i];
p->number_of_children = n;
return p;
}
我想创建具有多个 children 的树并创建每个节点都有一个函数。这是我的节点结构:
typedef struct node {
char *string; // Name of the node
int number_of_children;
struct node *children[];
} node;
这是我创建新节点的函数(有四个 children):
node *add_node(char *string, node *left, node *middle_left, node *middle_right, node *right) {
node *p;
if ((p = malloc(sizeof(node))) == NULL) yyerror("Memory error");
p->string = strdup(string);
p->children[p->number_of_children] = left;
p->children[p->number_of_children + 1] = middle_left;
p->children[p->number_of_children + 2] = middle_right;
p->children[p->number_of_children + 3] = right;
p->number_of_children = 4;
return p;
}
当我运行 p->string
下的这个函数值被更改为一些垃圾。当我想设置固定数量的 children(在我将 struct node *children[]
更改为 struct node *children[4]
的结构中)时,出现分段错误。你有什么想法?
你定义结构的方式,你必须事先知道你可以有多少children,因为你必须明确地为最后一个成员分配内存:
node *p = malloc(sizeof(node) + nchildren * sizeof(node));
您可以稍后重新分配以容纳更多节点,但这不是一种可行的方法,因为重新分配的内存的句柄可能会更改,这会破坏树连接。
有更好的方法让节点具有不同数量的 children,例如:
- 设定一个固定的最大 children 数,也许是 4 个,并使成员数组
children
明确地为 4 个元素长。保留一个数字,告诉您有多少 children 是有效的。 - 使 children 的列表成为动态分配的数组,以后可以重新分配。这意味着您有一个 two-level 分配:首先是正确的节点,然后是其 children. 的列表
- 保留 child 个节点的链表。想一想,您可以重新排列树,使每个节点都有一个
child
作为其最旧的 child 节点,并为每个节点的下一个最老的兄弟节点提供一个sibling
节点。
我不确定你到底想做什么,但第一种方法似乎对你来说是最简单的。
您的代码还有其他几个错误:
您应该只在创建节点时分配内存。显然,您已经将有效的节点句柄传递给您的函数。这些节点已经分配了内存或者它们是
NULL
。以下:p = malloc(sizeof(*p)); p = pref;
将分配内存并立即失去该内存的唯一句柄,这是内存泄漏。这里不用分配,直接说
p = pref
.malloc
分配的内存未初始化;它包含垃圾。在使用之前初始化所有结构成员。或者,考虑使用calloc
,它分配内存并将其清零。if ((p = malloc(sizeof(node))) == NULL)
中的组合赋值和检查是有效的,但阅读起来很复杂。在我看来,最好将它们分为分配和后续检查。 (毕竟,它只是以一些额外的括号为代价节省了您两次输入p
。)
您按数组传入可变数量的儿童的示例可能如下所示:
typedef struct node {
char *string;
size_t number_of_children;
struct node *children[4];
} node;
node *add_node(char *string, node *child[], size_t n)
{
node *p = malloc(sizeof(*p));
size_t i;
if (p == NULL) yyerror("Memory error");
p->string = strdup(string);
for (i = 0; i < n; i++) p->children[i] = child[i];
p->number_of_children = n;
return p;
}