Unload()递归C Segfault(类似TRIE的数据库)CS50 pset5
编辑1Unload()递归C Segfault(类似TRIE的数据库)CS50 pset5,c,dictionary,recursion,trie,cs50,C,Dictionary,Recursion,Trie,Cs50,编辑1 正如我所建议的,我现在不使用以下划线开头的名称,也不使用在-> \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu强 当试图用递归函数释放struct时,我遇到了一个segfault 这是我的结构: //creating new trie data ctructure typedef
正如我所建议的,我现在不使用以下划线开头的名称,也不使用在
->
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu强 当试图用递归函数释放struct时,我遇到了一个segfault 这是我的结构:
//creating new trie data ctructure
typedef struct dict
{
bool is_word;
struct dict *children[ALPHABET+1];
}
node;
它用于存储字典,字典用于拼写检查。在程序结束时,我需要释放所有分配的内存
这是我写的函数。它应该称自己是自由的,一块一块地释放。然而,在多次调用自己之后,它给了我segfault
bool unload(void)
{
// Check if root
if (temp == root)
{
for (int i = 0; i < ALPHABET+1; i++)
{
if (!temp->children[i] && i != ALPHABET)
{
}
else if (!temp->children[i] && i == ALPHABET)
{
free(temp);
return true;
}
else if(temp->children[i])
{
temp = temp->children[i];
unload();
}
}
}
else
{
for (int i = 0; i < ALPHABET+1; i++)
{
if (!temp->children[i] && i != ALPHABET)
{
}
else if (!temp->children[i] && i == ALPHABET)
{
temp1 = temp;
temp->children[i] = temp;
free(temp1);
return true;
}
else if (temp->children[i])
{
temp = temp->children[i];
unload();
}
}
}
return false;
}
bool卸载(无效)
{
//检查是否为根
if(temp==根)
{
对于(int i=0;ichildren[i]&&i!=字母表)
{
}
如果(!temp->children[i]&&i==字母表),则为else
{
免费(临时);
返回true;
}
否则如果(临时->儿童[i])
{
temp=temp->children[i];
卸载();
}
}
}
其他的
{
对于(int i=0;ichildren[i]&&i!=字母表)
{
}
如果(!temp->children[i]&&i==字母表),则为else
{
temp1=temp;
temp->children[i]=temp;
免费(temp1);
返回true;
}
否则如果(临时->儿童[i])
{
temp=temp->children[i];
卸载();
}
}
}
返回false;
}
假设root、temp、temp1是全局的。它们都是struct dict.当函数第一次被调用时,temp==root。您的代码正在演示为什么全局变量是一个坏主意,而且会适得其反。您应该将要释放的节点传递给函数;初始调用传递根节点。函数不需要访问任何全局变量 还要注意的是,点
和箭头->
运算符绑定得非常紧密,不应在其周围写任何空格。此外,以下划线开头的名称基本上保留供实现使用。比那还多,但不多。最简单的方法是避免在您发明的名称中使用下划线。仅用于访问系统提供的设施
此代码执行必要的操作,假设分配节点的代码确保所有指针为空
#include <stdlib.h>
#include <stdbool.h>
enum { ALPHABET = 26 };
typedef struct dict
{
bool is_word;
struct dict *children[ALPHABET+1];
} node;
void unload(node *item);
void unload(node *item)
{
for (int i = 0; i < ALPHABET+1; i++)
{
if (item->children[i] != 0)
unload(item->children[i]);
}
free(item);
}
此代码尚未运行。我已经为其他人在CS50问题上的变体编写了类似的函数。不需要比这更复杂。欢迎使用堆栈溢出。请尽快阅读和页面,但更重要的是,请阅读如何创建MCVE()。这里MCVE的关键要求之一是触发问题的一组紧凑的输入值。那好像不见了。非常感谢你的建议!我希望现在更好?您的代码正在演示为什么全局变量是个坏主意,而且会适得其反。您应该将要释放的节点传递给函数;初始调用传递根节点。该函数不需要访问任何全局变量。实际上,根据赋值,我不应该更改卸载声明(即bool unload(void)
),这就是使用全局变量的原因。节点分配部分如下所示:node*getnode(void){node*newnode=malloc(sizeof(node));if(newnode!=NULL){newnode->is_word=false;for(int i=0;i<(ALPHABET+1);i++)newnode->children[i]=NULL;}return newnode;}确定:写入:bool unload(void){real\u unload(root);返回true;}
并将我编写的函数重命名为real\u unload()
。或者可以使用:bool unload(void){if(root==NULL)返回false;real_unload(root);返回true;}
。不要创建名为temp
的全局变量。使用globals的时间不要过长。干净利落地完成工作-如图所示。不要告诉我-除了指定的函数外,您不允许编写其他函数?在这种情况下,你有一个虐待狂导师。这是CS50;你确定你引用的规则吗?以前没有人抱怨过类似的释放代码,但是……不,只是我不能更改特定函数的实现(比如卸载)。我们可以自由编写任何我们认为有用的函数。所以我会记住你的这个把戏!
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes -Wold-style-definition -c tr47.c
$