简体   繁体   中英

Why do none of my nodes get freed? (cs50 pset5 segmentation fault) C

I am having trouble implementing my load and unload functions in pset5 of the cs50 class at Harvard. When I run it, I get a segmentation fault and when I run valgrind, it tells me that none of the nodes that I malloc'd at load were freed.

I've been trying to fix this for days, I've tried several different implementations for my unload function, but nothing's worked. I think the mistake might be in my load function. Would someone please please please help me with this one?

 /****************************************************************************
 * 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;
 }

Your unload function is good. The problem with your code is the check function, notably the part where you try to convert the input to lower case:

char temp[LENGTH + 1];

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

There are two issues here. First, temp is not null-terminated. Second, the check for isalpha means you could leave characters uninitialised: If your input is, say, "I'm" , temp will hold 'I' , garbage , 'm' , garbage when it should hold 'I' , ' \\'' , 'm' , '\\0' , garbage .

Alternatively, you can filter out unwanted characters. In that case, you need two indices: one for the source word, another for the filtered word.

But you don't even need this additional step, because you hash function converts the input to toupper again.

Speaking of your hash function: You might want to pick a better one. The current one doesn't distribute the values well over the 5000 slots. (How are you even going to reach 5000 when you add, what?, up to 20 numbers between 0 and 25?)

The hash also has another problem: If you input a number, the contributing "letters" are negative, because in ASCII, numbers have values from 48 to 57 and you subtract the value of 'A' , 65, from them. In general, your hash function should return an unsigned value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM