0

I had posted two questions regarding memory leak in Problem Set 5, available at CS50 PSET5 Memory Leak and CS50 PSET5 Memory Leak (updated). I got amazing feedback from different people (and I appreciate all the helps!!!) but still hasn't been able to solve the problem. I have made all the changes suggested by those who commented on my two previous posts, but memories are still leaking. I have attached the latest code as well as the latest valgrind report below. PLEASE PLEASE PLEASE HELP. I can't thank enough but thank you!

Latest code:

// Have two new lines between regular text and block of code
/**
 * dictionary.c
 *
 * Computer Science 50
 * Problem Set 5
 *
 * Implements a dictionary's functionality.
 */

#include <cs50.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include "dictionary.h"

int word_count = 0;

// creation of node
typedef struct node
{
    char word_dict[LENGTH + 1];
    struct node *next;
}
node;

// creation of hash table and definition of hash function (reference: https://cs50.stackexchange.com/questions/9460/pset5-speller-c-check)
node* hashtables[26];
int hash_function (const char *key)
{
    int index = ((int)(key[0]) - 'a') % 26;
    return index;
}

/**
 * Returns true if word is in dictionary else false.
 */
bool check (const char* word)
{
    // satisfy case-insensitive requirement by changing all words prompted by the user into purely small caps
    char small_cap[LENGTH + 1];
    for (int i = 0; i < (strlen(word)); i++)
    {   
        if (isupper(word[i]))
            small_cap[i] = tolower(word[i]);
        else
            small_cap[i] = word[i];
    }

    small_cap[strlen(word)] = '\0';

    // hash the word prompted by th user and obtain the resulting index
    int index_text = hash_function(small_cap);

    // string-compare the word entered by the user with words from the dictionary
    node* cursor = hashtables[index_text];
    if (index_text >= 0)
    {
        while(cursor != NULL)
        {
            if(strcmp(cursor -> word_dict, small_cap) == 0)
                return true;
            else
                cursor = cursor -> next;
        }
    }
    return false;
}

/**
 * Loads dictionary into memory.  Returns true if successful else false.
 */
bool load (const char* dictionary)
{
    // open the dictionary, as per the user, in the read mode
    FILE* fp_dict = fopen(dictionary, "r");

    // make sure the dictionary is opened successfully
    node* head = NULL;
    if (fp_dict == NULL)
        printf("Unable to open dictionary\n");

    // perform a while loop whenever it is not the end of the dictionary yet
    while(!feof(fp_dict))
    {
       // make a new pointer pointing to a new word from the dictionary and scan the word this pointer points to
       node* new_node = malloc(sizeof(node));
       fscanf(fp_dict, "%s", new_node -> word_dict);

       if(feof(fp_dict))
       {
           free(new_node);
           break;
       }

       // hash the word from the dictionary and obtain the resulting index
       int hash = hash_function(new_node -> word_dict);

       // make sure hash is an integer equally to or larger than zero
       if (hash >= 0)
       {
           // the following condition returns true when no word_dict has been linked to the index of interest yet, or in other words, when the bucket of interst is empty
           if (hashtables[hash] == NULL)
           {
               hashtables[hash] = new_node;
               head = hashtables[hash];
               head -> next = NULL;
               word_count++;
           }
           else // hashtables[hash] != NULL, or in other words, the "bucket" of interest is not empty
           {
               new_node -> next = head;
               head = new_node;
               hashtables[hash] = head;
               word_count++;
           }
           // note that in either case above, the new node is always inserted to the very beginning to the linked list
       }
    }

    // once the end of the dictionary is reached
    if (feof(fp_dict))
    {
        fclose(fp_dict);
        return true;
    }

    fclose(fp_dict);
    return false;
}

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

/**
 * Unloads dictionary from memory.  Returns true if successful else false.
 */
bool unload(void)
{
    int unload_check = 0;
    // the while loop frees linked lists one by one, down the hash table
    while (unload_check < 26)
    {
        node* unload_cursor = hashtables[unload_check];
        if (unload_cursor == NULL)
        {
            unload_check++;
        }
        else
        {
            while (unload_cursor -> next != NULL)
            {
                node* temp = unload_cursor;
                unload_cursor = unload_cursor -> next;
                free(temp);
            }
            unload_check++;
        }
    }
    return true;
}

Latest valgrind report:

==1982== 
==1982== HEAP SUMMARY:
==1982==     in use at exit: 1,076,152 bytes in 19,217 blocks
==1982==   total heap usage: 162,284 allocs, 143,067 frees, 9,088,928 bytes allocated
==1982== 
==1982== Searching for pointers to 19,217 not-freed blocks
==1982== Checked 78,328 bytes
==1982== 
==1982== 1,512 bytes in 27 blocks are definitely lost in loss record 1 of 2
==1982==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1982==    by 0x40136F: load (dictionary.c:88)
==1982==    by 0x400A0D: main (speller.c:45)
==1982== 
==1982== 1,074,640 bytes in 19,190 blocks are definitely lost in loss record 2 of 2
==1982==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1982==    by 0x40126E: check (dictionary.c:56)
==1982==    by 0x400D97: main (speller.c:117)
==1982== 
==1982== LEAK SUMMARY:
==1982==    definitely lost: 1,076,152 bytes in 19,217 blocks
==1982==    indirectly lost: 0 bytes in 0 blocks
==1982==      possibly lost: 0 bytes in 0 blocks
==1982==    still reachable: 0 bytes in 0 blocks
==1982==         suppressed: 0 bytes in 0 blocks
==1982== 
==1982== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
==1982== 
==1982== 1 errors in context 1 of 3:
==1982== Conditional jump or move depends on uninitialised value(s)
==1982==    at 0x4013A3: load (dictionary.c:95)
==1982==    by 0x400A0D: main (speller.c:45)
==1982==  Uninitialised value was created by a heap allocation
==1982==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1982==    by 0x40136F: load (dictionary.c:88)
==1982==    by 0x400A0D: main (speller.c:45)
==1982== 
==1982== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
0

Your error is in the statement in the function load

 while (unload_cursor -> next != NULL)

it should be

while (unload_cursor != NULL)

I'll let you think why

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .