【问题标题】:Can someone help me find the segfault here?有人可以帮我在这里找到段错误吗?
【发布时间】:2016-07-18 13:18:57
【问题描述】:

编辑:所以,'index' 没有被返回到 0。那么。这修复了一个段错误。但仍然得到不同的段错误。正在努力。

node* new_node(void){
    node* ptr = malloc(sizeof(node));
    for (int i = 0; i<27; i++) {
        ptr->next[i] = NULL;
    }
    return ptr;
}
bool load(const char* dictionary)
{
    FILE* dict = fopen(dictionary, "r");
    node* ptr = new_node;
    char word[LENGTH+1];
    int index = 0;
    for (int c = fgetc(dict); c!=EOF; c = fgetc(dict)){
        if(c!='\n'){
            word[index]=c;
            index++;
        }
        else {
            for(int x=0; x<=index; x++){
                int ch = (word[x] == '\'') ? 26 : tolower(word[x])-'a';
                if (ptr->next[ch] == NULL){
                    ptr->next[ch] = new_node;
                }
                ptr = ptr->next[ch];
            }
            ptr->end=true;
        }
    }
    return true;
}

我正在尝试为字典实现 trie 数据结构,但我的程序似乎在此函数的某处出现了段错误。即使在 GDB 的帮助下,我似乎也无法确定它,所以有人可以帮帮我吗?

节点定义如下:

typedef struct node{
    bool end;
    struct node* next[27];
} node;

字典文件:

a
aaa
aaas
aachen
aalborg
aalesund
aardvark
aardvark's
aardvarks
aardwolf

(...)

【问题讨论】:

  • 另外,请务必检查 malloc 是否正确返回...
  • @Haris 为什么会这样?分配的内存不应该是节点对象的大小吗?
  • @Haris 分配指针似乎并不正确。他为什么要这么做? “确切地说,节点对象 ptr 的类型是节点 * 而不是节点”是什么意思。 malloc 返回一个指向分配对象的指针......所以你的意思是......?
  • for (char c = fgetc(dict); c!=EOF; c = fgetc(dict)) 应该是 for (int c = fgetc(dict); c!=EOF; c = fgetc(dict))。您无法将char 与EOF 进行比较,因为EOF 值可能无法由char 值表示。
  • struct node* next[27]; 诶?为什么这个变量被称为 next?我以为这是某种链表? “下一个”应该是什么,某种查找表?这是一个链表吗?哈希表?

标签: c pointers segmentation-fault trie


【解决方案1】:

您应该多过滤一些标点符号\不支持的字符。 [a-z|A-Z|\n|\\] 之外的任何字符都会因为

而使您的程序崩溃
int ch = (word[x] == '\'') ? 26 : tolower(word[x])-'a';
if (ptr->next[ch] == NULL){

鉴于您打开文件,某处可能有空格或某些意外字符。你需要类似的东西

    if(c!='\n'){
        int num = (c == '\'') ? 26 : tolower(c)-'a');
        if(num >=0 && num < 27)
        {
           word[index]=c;
           index++;
        }
    }

【讨论】:

    【解决方案2】:

    您忘记在循环开始时将index 重置为0。

    您还应该使用calloc(1, sizeof(node)) 而不是malloc(sizeof(node)) 以避免内存未初始化。我建议您使用valgrind 来帮助您在代码中跟踪此类问题。

    【讨论】:

      【解决方案3】:

      您的代码中有很多问题:

      • 当您使用malloc 分配内存时,它是未初始化的。在分配它之后直接初始化它,这样NULL 指针真的是空的。 (calloc,'malloc' 的表亲,将所有内存初始化为零。)

      • 当你遍历这个词时,你也不应该包括index:

        for (int x = 0; x < index; x++) ...
        
      • 当你找到一个词的结尾时,你必须将index 重置为0。否则,你将追加到旧词并溢出缓冲区。 (您可能还应该强制执行“index”的上限。)

      • 同样,当您在 trie 中插入一个单词时,您必须重置指针以将 trie 遍历到 trie 的根。这里需要两个指针:一个根节点指针和一个用于遍历 trie 的辅助指针。

      • 按原样,您的 trie 对于您的函数是本地的。返回根节点,以便其他函数可以使用trie,或者NULL失败。

      修复这些,您将拥有一个不崩溃的功能。 (它仍然会泄漏内存,并且可能无法正确构造 trie。)

          node *load(const char *dictionary)
          {
              FILE *dict = fopen(dictionary, "r");
              node *head = calloc(1, sizeof(node));
      
              char word[LENGTH + 1];
              int index = 0;
      
              for (int c = fgetc(dict); c != EOF; c = fgetc(dict)) {
                  if (c != '\n') {
                      word[index] = c;
                      index++;
                  } else {
                      node *ptr = head;
      
                      for (int x = 0; x < index; x++) {
                          int ch = (word[x] == '\'') ? 26 : tolower(word[x]) - 'a';
                          if (ptr->next[ch] == NULL) {
                              ptr->next[ch] = calloc(1, sizeof(node));
                          }
                          ptr = ptr->next[ch];
                      }
                      ptr->end = true;
                      index = 0;
                  }
              }
      
              return head;
          }
      

      【讨论】:

      • 好。正如我所说:segfault和内存错误都没有了,但是我没有检查代码是否正常工作。
      • 我解决了这个问题,并设法让整个拼写检查器的工作速度仅比 cs50 员工的示例慢 0.01 秒。现在也要把它剃掉。
      • 好!但是 0.01 秒很可能是一个测量误差。并首先关注正确性:产生错误结果的快速程序并不值得。 :)
      【解决方案4】:

      行:

      node* ptr = new_node;
      

      和

      ptr->next[ch] = new_node;
      

      不是调用函数,而是将函数的地址分配给ptr。改为调用该函数。

      如果启用了编译器警告:-Wall 和 -Wextra,则可以避免此问题。


      没有对数组word 进行边界检查。使用值LENGTH 在使用前检查索引是否在边界内。

      不清楚 for 循环中的 if 语句在做什么。似乎每次找到换行符时,都会将整个数组 word 添加到树中,但 index 不会重置,因此会多次添加相同的数组。在某些时候index 会指出越界导致未定义的行为。使用数组word 后,应重置index。

      【讨论】:

      • 谢谢,这是个问题,但是在我实现这个功能之前就发生了段错误
      猜你喜欢
      • 2016-07-09
      • 1970-01-01
      • 1970-01-01
      • 2019-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多