【问题标题】:Why do none of my nodes get freed? (cs50 pset5 segmentation fault) C为什么我的节点都没有被释放? (cs50 pset5 分段错误) C
【发布时间】:2016-01-06 22:47:07
【问题描述】:

我在哈佛 cs50 课程的 pset5 中实现加载和卸载函数时遇到问题。当我运行它时,我得到一个分段错误,当我运行 valgrind 时,它告诉我我在加载时 malloc 的所有节点都没有被释放。

我已经尝试解决这个问题好几天了,我已经为我的卸载功能尝试了几种不同的实现,但没有任何效果。我认为错误可能出在我的加载功能中。有人请帮我解决这个问题吗?

 /****************************************************************************
 * dictionary.c
 *
 * Computer Science 50
 * Problem Set 5
 *
 * Implements a dictionary's functionality.
  ***************************************************************************/

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

 #include "dictionary.h"

 #define HASHTABLE_SIZE 5000

 // create word counter for size
 int wordCount = 0;

 // linked link struct
 typedef struct node
 {
     // word's length + NULL character
     char word[LENGTH + 1];
     struct node* next;
 }
 node;

 // Hashtable array
 node* hashtable[HASHTABLE_SIZE];


 // hash function from study.cs50.net
 int hash_function(char* key)
 {
     // initialize index to 0
     int index = 0;   

     // sum ascii values
     for (int i = 0; key[i] != 0; i++)
     {
         index += toupper(key[i]) - 'A';
     }

     return index % HASHTABLE_SIZE;
 }

 /**
  * Returns true if word is in dictionary else false.
  */
 bool check(const char* word)
 {
     // create variable to hold word
     char temp[LENGTH + 1];

     // convert every character in word to lowercase
     for (int i = 0, n = strlen(word); i < n; i++)
     {
         if (isalpha(word[i]))
         {
             temp[i] = tolower(word[i]);  
         } 
     }

     // get hashed word's index
     int hash_index = hash_function(temp);

     // find head of that index
     node* head = hashtable[hash_index];


     // traverse through linked list 
     for (node* cur = head; cur != NULL; cur = cur->next)
     {
         // find if linnked list contains word
         if (strcmp(cur->word, word) == 0)
         {
             return true;
         }   
     }  

     return false;
 }

 /**
  * Loads dictionary into memory.  Returns true if successful else false.
  */
 bool load(const char* dictionary)
 {
     // // open file
     FILE* file = fopen(dictionary, "r");

     // check if file exists
     if (file == NULL)
     {
         return false;
     }

     // word length plus NULL character
     char word[LENGTH + 1];

     // iterate through every word of the dictionary
     while (fscanf(file, "%s\n", word) != EOF) // Source: http://stackoverflow.com/questions/6275558/question-about-whileeof
          {
              node* new_node = malloc(sizeof(node));

         if (new_node == NULL)
         {
             return false;
         }

         wordCount++;

         strcpy(new_node->word, word);  // Source: cs50 reddit

         int hash_index = hash_function(new_node->word);

         // check whether node should be head
         if (hashtable[hash_index] == NULL)
         {
             hashtable[hash_index] = new_node;
             new_node->next = NULL;
         }

         else
         {
             new_node->next = hashtable[hash_index];
             hashtable[hash_index] = new_node; 
         }    
     }
     // close file
     fclose(file);

     return false;
 }

 /**
  * 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)
 {   
     // go through all of the indexes in the hashtable 
     for (int i = 0; i < HASHTABLE_SIZE; i++)
     {
         node* head = hashtable[i];

         while (head != NULL)
         {
             node* ptr = head->next;

             free(head);
             head = ptr;
         }   
     }   
     return true;
 }

【问题讨论】:

  • 在调试器下运行它时发现了什么? “free”的命中次数和“malloc”的命中次数一样多吗?
  • 请出示您的main 函数和/或驱动完整程序的其他代码。否则我们不知道你是否打电话给unload(我不是说你不是,但简单的错误在 StackOverflow 上并不少见)。
  • 另外,刚刚想到你的程序可能在任何 free 调用完成之前就崩溃了,这就是 valgrind 报告没有释放的原因。如果是这样,那是一个转移注意力的问题,你关注的是错误的事情(泄漏)。而是专注于 segv。一种可能性是你得到了一些长词。您正在使用不安全的函数,例如 strcpyfscanf,没有检查以确保您的缓冲区没有溢出。
  • 如果您有分段错误,那么您不太可能到达调用unload 的地步。一个好的调试器会带你找到分段错误的根源。
  • 请按照已经建议的方式使用调试器。这应该可以准确地告诉您哪一行代码导致了 segv。如果您需要更多帮助,您需要显示更新的代码(否则我们不知道您的更改是否正确和完整)。但听起来你没有修复check 函数,它仍然存在temp 缓冲区溢出的问题。

标签: c memory-leaks segmentation-fault cs50


【解决方案1】:

您的unload 功能很好。您的代码的问题在于 check 函数,尤其是您尝试将输入转换为小写的部分:

char temp[LENGTH + 1];

for (int i = 0, n = strlen(word); i < n; i++)
{
    if (isalpha(word[i]))
    {
        temp[i] = tolower(word[i]);  
    } 
}

这里有两个问题。首先,temp 不是以空值结尾的。其次,检查isalpha 意味着您可以保留未初始化的字符:如果您的输入是"I'm"temp 将保留'I'垃圾'm'garbage 当它应该包含 'I'' \'''m''\0'garbage

或者,您可以过滤掉不需要的字符。在这种情况下,您需要两个索引:一个用于源词,另一个用于过滤后的词。

但是你甚至不需要这个额外的步骤,因为你的哈希函数再次将输入转换为toupper

谈到您的哈希函数:您可能想选择一个更好的。当前的值不能很好地分布在 5000 个插槽中。 (加起来怎么能达到 5000,什么?, 0 到 25 之间的最多 20 个数字?)

哈希还有另一个问题:如果你输入一个数字,贡献的“字母”是负数,因为在 ASCII 中,数字的值从 48 到 57,你从它们中减去 'A' 的值,即 65。一般来说,你的哈希函数应该返回一个无符号值。

【讨论】:

    猜你喜欢
    • 2019-07-14
    • 2018-01-11
    • 2022-06-16
    • 2021-07-23
    • 2021-07-31
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多