0

I'm working on Speller with a hashtable, and my code compiles, but when I run valgrind it tells me that I'm trying to use an uninitialized value.

==10864== Conditional jump or move depends on uninitialised value(s)
==10864==    at 0x40131F: check (dictionary.c:103)
==10864==    by 0x400CC9: main (speller.c:112)
==10864==  Uninitialised value was created by a heap allocation
==10864==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10864==    by 0x40115B: load (dictionary.c:59)
==10864==    by 0x4009A4: main (speller.c:40)
==10864== 

This error is located in my check function which looks like this (I marked the line with the error code with <-):

bool check(const char *word)
{
    // TODO
    int len = strlen(word);
    char *l = malloc(len + 1);
    for (int i = 0; i < len; i++)
    {
        l[i] = tolower(word[i]);
    }
    l[len] = '\0';

    int bucket = hash(l);
    node *cursor = hashtable[bucket];
    while (cursor != NULL)<-
    {
        if (strcasecmp(cursor->word, l) != 0)
        {
            cursor = cursor->next;
        }
        else
        {
            free(l);
            return true;
        }
    }
    free(l);
    return false;
}

Why does this happen? Shouldn't l have been assigned a value again before reaching that line?

If needed, my whole code looks like this:

// Implements a dictionary's functionality

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

#include "dictionary.h"

// Represents number of buckets in a hash table
#define N 26

// Represents a node in a hash table
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
}
node;

// Represents a hash table
node *hashtable[N];

int val = 0;


// Hashes word to a number between 0 and 25, inclusive, based on its first letter
unsigned int hash(const char *word)
{
    return tolower(word[0]) - 'a';
}

// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
    // Initialize hash table
    for (int i = 0; i < N; i++)
    {
        hashtable[i] = NULL;
    }

    // Open dictionary
    FILE *file = fopen(dictionary, "r");
    if (file == NULL)
    {
        unload();
        return false;
    }

    // Buffer for a word
    char buf[LENGTH + 1];

    // Insert words into hash table
    while (fscanf(file, "%s", buf) != EOF)
    {
        // TODO
        node *temp_node = malloc(sizeof(node));
        strncpy(temp_node->word, buf, sizeof(buf));
        int s = hash(buf);

        if (hashtable[s] == NULL)
        {
            hashtable[s] = temp_node;
        }
        else
        {
            temp_node->next = hashtable[s];
            hashtable[s] = temp_node;
        }
        val++;
    }

    // Close dictionary
    fclose(file);

    // Indicate success
    return true;
}

// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
    // TODO
    return val;
}
// Returns true if word is in dictionary else false
bool check(const char *word)
{
    // TODO
    int len = strlen(word);
    char *l = malloc(len + 1);
    for (int i = 0; i < len; i++)
    {
        l[i] = tolower(word[i]);
    }
    l[len] = '\0';

    int bucket = hash(l);
    node *cursor = hashtable[bucket];

    while (cursor != NULL)
    {
        if (strcasecmp(cursor->word, l) != 0)
        {
            cursor = cursor->next;
        }
        else
        {
            free(l);
            return true;
        }
    }
    free(l);
    return false;
}

void destruct(node *head)
{
    if (head->next != NULL)
    {
        destruct(head->next);
    }
    free(head);
}

// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
    // TODO
    for (int i = 0; i < N; i++)
    {
        if (hashtable[i] != NULL)
        {
            destruct(hashtable[i]);
        }
    }
    return true;
}
4
  • Were all of the hashtable[] elements initialized to NULL when it was created?
    – Cliff B
    Commented Sep 26, 2019 at 21:50
  • I think I did. @Cliff B
    – GAXY
    Commented Sep 26, 2019 at 22:46
  • I ran your code to test it. Couldn't duplicate your issues, but I did get other valgrind warnings. Is this the correct code? BTW, the code declares an unused var, x at line 102. And for the record, these are warnings, but there is no memory leak, at least when I tested.
    – Cliff B
    Commented Sep 27, 2019 at 2:03
  • This is definitely the correct code. The unused variable was leftover from me trying to fix the issue. Do you have any idea how I could fix this, it seems there might be an issue with my CS50 IDE. @Cliff B
    – GAXY
    Commented Sep 27, 2019 at 5:30

1 Answer 1

0

Actually, it was pretty straightforward. When I ran it, valgrind generated the following report:

==445== Conditional jump or move depends on uninitialised value(s)
==445==    at 0x401395: destruct (dictionary.c:121)
==445==    by 0x40124C: unload (dictionary.c:136)
==445==    by 0x400E49: main (speller.c:152)
==445==  Uninitialised value was created by a heap allocation
==445==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==445==    by 0x40115B: load (dictionary.c:59)
==445==    by 0x4009A4: main (speller.c:40)
==445== 
==445== Conditional jump or move depends on uninitialised value(s)
==445==    at 0x401395: destruct (dictionary.c:121)
==445==    by 0x4013A7: destruct (dictionary.c:123)
==445==    by 0x4013A7: destruct (dictionary.c:123)
==445==    by 0x4013A7: destruct (dictionary.c:123)
==445==    by 0x4013A7: destruct (dictionary.c:123)
==445==    by 0x40124C: unload (dictionary.c:136)
==445==    by 0x400E49: main (speller.c:152)
==445==  Uninitialised value was created by a heap allocation
==445==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==445==    by 0x40115B: load (dictionary.c:59)
==445==    by 0x4009A4: main (speller.c:40)
==445== 
==445== Conditional jump or move depends on uninitialised value(s)
==445==    at 0x401395: destruct (dictionary.c:121)
==445==    by 0x4013A7: destruct (dictionary.c:123)
==445==    by 0x40124C: unload (dictionary.c:136)
==445==    by 0x400E49: main (speller.c:152)
==445==  Uninitialised value was created by a heap allocation
==445==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==445==    by 0x40115B: load (dictionary.c:59)
==445==    by 0x4009A4: main (speller.c:40)
==445== 

Everything in that report points back to uninitialized values. Many point to the unload and destruct functions, but that's too far downstream. Looking further, the earliest occurring issue (repeated twice) is in the load function, indicated by by 0x40115B: load (dictionary.c:59). Line 59 is this:

    node *temp_node = malloc(sizeof(node));

The malloc() call allocates memory for the node but does NOT initialize anything. Any memory address pointers will contain garbage data, not a valid address and not NULL. When a node is created, each of it's elements need to be initialized. While the contents of the word element are set in the next line, there's no code to set the ->next pointer.

Hint: calloc is your friend.

If this answers your question, please click on the check mark to accept. Let's keep up on forum maintenance. ;-)

You must log in to answer this question.

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