C 中 Trie 实现中的分段错误

Posted

技术标签:

【中文标题】C 中 Trie 实现中的分段错误【英文标题】:Segmentation Fault in Trie implementation in C 【发布时间】:2017-08-19 00:40:11 【问题描述】:

我正在尝试实现一个 trie 数据结构来对给定的文本文件进行拼写检查。目前,它似乎适用于文件中的几个单词,然后它达到了段错误。我尝试调试以找到罪魁祸首,但我发现“字母”的值保留了看似随机的负值(它应该在 1 到 27 之间,包括在内)。通常,在我启动程序后,seg fault 问题几乎立即出现,所以我不确定为什么这个问题会在程序中间弹出。

    /**
     * Implements a dictionary's functionality.
     */

    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>

    #include "dictionary.h"


    //create global root node
    Trienode *root;
    //create word counter for size() function
    unsigned int wordcount = 0;

    //creates an empty node
    Trienode * newnode()
    
        Trienode *nnode = NULL;
        nnode = (Trienode *)malloc(sizeof(Trienode));
        //initialize new node with null pointers and values
        nnode -> parent = NULL;
        for(int i = 0; i < 27; i++)
        
            nnode -> children[i] = NULL;
        
        return nnode;
    

    void cleartrie(Trienode *head)
    
        //if child node exists, free it, else continue with next iteration in for loop
        if(head)
        
            for(int i = 0; i < 27; i++)
            
                cleartrie(head -> children[i]);
            
            free(head);
            head = NULL;
        
    

    /**
     * Returns true if word is in dictionary else false.
     */
    bool check(const char *word)
    
        int i = 0;
        int letter;
        Trienode *head = root;

        while(word[i] != '\0')
        
            if(isalpha(word[i]))
            
                letter = word[i] - 'a';
            
            else //it must be an apostrophe
            
                letter = word[i] - 13;
            
            if(!(head -> children[letter]))
            
                return false;
            
            else //a pointer must exist
            
                head = head -> children[letter];
            
            i++;
        
        return true;
    

    /**
     * Loads dictionary into memory. Returns true if successful else false.
     */
    bool load(const char *dictionary)
    
        //open file
        FILE *infile = fopen(dictionary, "r");
        Trienode *parnode; //parent node
        root = newnode();
        Trienode *curnode = root; //current node

        int letter = 0;
        //while not end of file, read words
        while(fgetc(infile) != EOF)
        
            //while not end of word, read letters
            for(;;)
            
                int c;
                //read current letter in file
                c = fgetc(infile); 
                //convert input char to corresponding array location (a - z = 0-25, apostrophe = 26)
                if(isalpha(c))
                
                    letter = c - 'a';
                
                else if (c == '\'')
                
                    letter = c - 13;
                
                //if end of string, exit loop
                else if (c == '\0')
                
                    //end of word, so endofstring = true
                    wordcount++;
                    break;
                
                //move to next letter if not either apostrophe or alphabetical
                else
                
                    break;
                
                //if pointer to letter of word doesn't exist, create new node
                if(curnode -> children[letter] == NULL)
                
                    curnode -> children[letter] = newnode();
                
                //child node is the new current node
                parnode = curnode;
                curnode = curnode -> children[letter];
                curnode -> parent = parnode;

            
            //return to root node
            curnode = root;
         

        fclose(infile);
        return true;
    


    /**
     * Returns number of words in dictionary if loaded else 0 if not yet loaded.
     */
    unsigned int size(void)
    
        return wordcount;
    

    /**
     * Unloads dictionary from memory. Returns true if successful else false.
     */
    bool unload(void)
    
        cleartrie(root);
        if (root == NULL)
        
            return true;
        
        return false;
    

对文字墙感到抱歉,但大部分内容只是为了提供上下文(我希望如此)。段错误错误发生在检查辅助函数的 if(!(head -> children[letter])) 行。

提前致谢!

【问题讨论】:

我看不到您的代码中的 Trienode 是什么。是结构体吗? 您使用了调试器吗?在您的代码中的哪些点准确地发生了设置错误(调试器应该告诉您)?您在哪里看到 letter 的值出错了?由于这是部分代码列表(我不建议在不缩小问题范围的情况下将所有代码都放入其中)很难说问题只是“查看”它的位置。进行更多调试并缩小范围。 使用 valgrind。它会告诉你问题出在哪里。 请注意,点. 和箭头-&gt; 运算符绑定得非常紧密,不应在两边写上空格。 【参考方案1】:

我怀疑您的测试文件可能包含一些大写字母。如果是这种情况,那么减去'a' 以尝试重新映射您的字母将导致负数,因为'A' &lt; 'a'。看看ASCII Table。首先将字母转换为小写应该可以解决您的问题。

【讨论】:

以上是关于C 中 Trie 实现中的分段错误的主要内容,如果未能解决你的问题,请参考以下文章

loop-malloc.c 的向量中的分段错误:没有这样的文件或目录

C ++中链表实现中的分段错误

由于 C 中的内存不足导致的分段错误

mov指令中的分段错误

带有 std::promise 的 C++11 分段错误

为啥写作主要;在 C 中给出一个段错误