Search code examples
cvalgrindcs50

Valgrind: Conditional jump or move depends on uninitialised value(s). Is not terminating a string inside a longer array with '\0' enough?


The problem set 5 of cs50, speller, ask to implement some dictionary functionalities. I get this warnings with valgrind:

==393== Conditional jump or move depends on uninitialised value(s)
==393==    at 0x49DB143: tolower (ctype.c:46)
==393==    by 0x483F864: strcasecmp (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==393==    by 0x4019CF: check (dictionary.c:42)
==393==    by 0x401603: main (speller.c:114)
==393==  Uninitialised value was created by a heap allocation
==393==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==393==    by 0x401AF8: load (dictionary.c:82)
==393==    by 0x40129E: main (speller.c:40).

Here it is my dictionary.c file, which contains the helpers function.

// Implements a dictionary's functionality

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dictionary.h"
#include <strings.h>

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

// Number of buckets in hash table
const unsigned int N = 1;

unsigned int siz = 0;
// Hash table
node *table[N];
/*
for (int i = 0; i < N; i++)
{
    table[i]->next
}
*/

// Returns true if word is in dictionary, else false
bool check(const char *word)
{
    // TODO

    int h = hash(word);

    node *ll = table[h];
    while (ll != NULL)
    {

        if (strcasecmp(ll->word, word) == 0)
        {
            printf("len: %lu\n", strlen(ll->word));
            return true;
        }
        ll = ll->next;
    }
    return false;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    // TODO
    return 0;
}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    printf("a\n");
    // TODO
    FILE *input = fopen(dictionary, "r");
    if (input == NULL)
    {
        printf("Could not open file.\n");
        return false;
    }
    printf("b\n");
    char c;
    int i = 0;
    unsigned int h = 0;
    char word[LENGTH + 1];
    node *key = NULL;

    while (fread(&c, sizeof(char), 1, input))
    {

        if (i == 0)
        {
            key = malloc(sizeof(node));
        }

        else if (c == '\n')
        {
            word[i] = '\0';
            i = 0;
            h = hash(word);
            key->next = table[h];
            table[h] = key;
            siz++;
            continue;
        }
        key->word[i] = c;
        i++;

    }
    fclose(input);
    /*
    for (int j = 0; j < N; j++)
    {
        while (table[j] != NULL)
        {
            printf("%s ->\n", table[j]->word);
            table[j] = table[j]->next;
        }
    }
    */
    return true;
}

// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    // TODO
    return siz;
}

// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
    // TODO
    node *n = NULL;
    node *tmp = NULL;
    for (int i = 0; i < N; i++)
    {
        n = table[i];
        while (n != NULL)
        {
            tmp = n;
            n = n->next;
            free(tmp);
        }
    }
    return true;
}

I think this warning happens because at line 42 the function strcasecmp is trying to lowercase some characters of the string node->word, even after the '\0'. In fact, the warnings go away if I substitute, in load(), key = malloc(sizeof(node)); with key = calloc(1, sizeof(node)); Because calloc allocate and also sets the memory to 0.

So, my question is: how strcasecmp really work?? In the malloc scenario ll->word, the argument I pass to strcasecmp, is an array of chars like this "actual_valid_chars \0 garbage bytes until array length: LENGTH". In the calloc scenario the array is "actual_valid_chars \0 000000...".

So, I think that at I am passing uninitialized memory to strcasecmp, in case of malloc, but I also terminate the string with \0 in both scenarios.

Shouldn't strcasecmp() recognize the end of the string by '\0' even if it is in the middle of the array? Can someone clarify these passages to me please? Also, can I simply ignore these warnings, or are there better practices to use?


Solution

  • key->word is not null terminated in the load function. The char array word is, though it's never populated.