1

This is the full code:

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


#include "dictionary.h"

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

// TODO: Choose number of buckets in hash table
const unsigned int N = 26;

// Hash table
node *table[N];

unsigned int wordss = 0;

// Returns true if word is in dictionary, else false
bool check(const char *word)
{
    // TODO
    int index1 = hash(word);
    node *cursor = table[index1];
    while (cursor != NULL)
    {
        if (strcasecmp(cursor->word, word) == 0)
        {
            return true;
        }
        cursor = cursor-> next;
    }
    return false;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    // TODO: Improve this hash function

     long sum = 0;
      for (int i = 0; i < strlen(word); i++)
    {
        sum += tolower(word[i]);
    }
    return sum % N;

}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    // TODO
    FILE *input = fopen(dictionary, "r");
    if (input == NULL)
    {
        printf("Could not open file.\n");
        return false;
    }

    char word1[LENGTH+1];
    while (fscanf(input, "%s", word1) != EOF) {
         node *n = malloc(sizeof(node));
    if (n == NULL)
    {
        printf("couldnt allocate memory \n");
        return false;
    }
    strcpy(n->word, word1);
    int index = hash(n->word);
    if(table[index] != NULL)
    {
        n->next = table[index];
        table[index] = n;
    }
    else
    {
        table[index] = n;
    }
    wordss = wordss + 1;
    }
    if (fscanf(input, "%s", word1) == EOF)
    {
        fclose(input);
        return true;
    }
    fclose(input);
    return false;
}

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

    return wordss;
}

// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
    // TODO
    for (int i = 0; i < N; i++)
    {

        node *cursor = table[i];
        while (cursor != NULL)
        {
            node *temp = cursor;
            cursor = cursor->next;
            free(temp);
        }
    return true;
}
}
5
  • please put your code through style50 first. your indentation is off and very confusing.
    – UpAndAdam
    Commented Jan 18 at 2:25
  • your load and unload are both completely broken with missing bracket and horrible indentation.
    – UpAndAdam
    Commented Jan 18 at 2:29
  • hello, it's not the exact same; I deleted the faulty parts from Unload and posted my full code like the other commenter requested. Also, i see no missing brackets? Sorry if my code looks unaesthetic; I am still learning, if I wasnt I wouldnt be posting here :( Also about your other answer; I deleted the freeing NULL pointer part. And now my Unload function looks almost the same as code from other people I actually ended up looking up. Yet the last condition in check50 still fails. Sorry if my indentation is off but its easier to read for me personally. have a good one
    – Rae
    Commented Jan 18 at 15:22
  • See my answer below where I will show the properly indented and fixed version of your code..
    – UpAndAdam
    Commented Jan 18 at 16:53
  • Your indentation can't be helping you read it when its wrong. You can pick whatever style you like, but you need to do it consistently. You weren't consistent and it revealed an error you made. I've changed my mind about the second part, its better habit to ask a new question, I would link to the prior question in this question though. For indentation, just use style50 for now, it will get you in good habits. There are some things I dont agree with about the style50 format but its one less thing for you to have to worry about for now, and makes your code easy to read and review.
    – UpAndAdam
    Commented Jan 18 at 17:27

1 Answer 1

2

I will add comments for any missing braces I add or indentation I change or for code I would change like so // NOTE blah blah blah

#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
    
#include "dictionary.h"
    
// Represents a node in a hash table
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
} node;
    
// TODO: Choose number of buckets in hash table
const unsigned int N = 26;
   
// Hash table
node *table[N];
    
unsigned int wordss = 0;
    
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
    // TODO
    int index1 = hash(word);
    node *cursor = table[index1];
    while (cursor != NULL)
    {
        if (strcasecmp(cursor->word, word) == 0)
        {
            return true;
        }
        cursor = cursor-> next;
    }
    return false;
}
    
// Hashes word to a number
unsigned int hash(const char *word)
{
    // TODO: Improve this hash function
    
    long sum = 0; // NOTE should probably make this unsigned
    for (int i = 0; i < strlen(word); i++)
    {
        sum += tolower(word[i]);
    }
    return sum % N; // NOTE technically you should cast this result back to unsigned int. 
}
    
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    // TODO
    FILE *input = fopen(dictionary, "r");
    if (input == NULL)
    {
        printf("Could not open file.\n");
        return false;
    }
    
    char word1[LENGTH+1];
    while (fscanf(input, "%s", word1) != EOF) 
    { // NOTE moved brace to new line for consistency
        node *n = malloc(sizeof(node));
        if (n == NULL)
        {
            printf("couldnt allocate memory \n");
            return false; // NOTE you forgot to close the file before returning
        }
        strcpy(n->word, word1);
        int index = hash(n->word);
        
        if (table[index] != NULL)
        {
            n->next = table[index];
            table[index] = n;
        }
        else
        {
            table[index] = n;
        }
        
        wordss = wordss + 1; // NOTE why not use words++ or wordss += 1??
    } // NOTE moved this brace to proper indent level
    
    // NOTE this now properly indented section is outside of the while loop
    // NOTE this check is not needed, this failure is what got you out of the while loop in the first place
    if (fscanf(input, "%s", word1) == EOF) // NOTE per above this should be removed.
    { // NOTE remove
        fclose(input); // NOTE keep but unindent
        return true; // NOTE keep but unindent
    } // NOTE remove
    fclose(input); // NOTE remove
    return false;  // NOTE remove
}
    
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    // TODO
    return wordss;
}
    
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
    // TODO
    for (int i = 0; i < N; i++)
    {
        node *cursor = table[i];
        while (cursor != NULL)
        {
            node *temp = cursor;
            cursor = cursor->next;
            free(temp);
        }
        return true; // NOTE this return should be outside the for loop!
    }
}

Thoughts: outside of the blatant error in unload where you are returning from the wrong spot which is absolutely going to prevent you from freeing all of the memory this code should pass the tests, and is functional. Not good, but should be functional.

You have a number of ways you could improve this, most notably would be considering keeping the node's in a given list in your table sorted in some fashion so that you don't have to search an entire 'basket' for each check every single time. That would help you regardless of your hash function, unless you are certain that your hash is so good that you have next to no hash collisions. With only 26 buckets, and more than 26 words you know that you will have significant hash collisions. But lets save that part for later. The focus is on you just getting some thing that functions basically and without memory errors.

Notes:

  • check(...): looks good
  • hash(...): looks good. A better hash function would be interesting but this should pass and works with your load and check function
  • load(...) : looks mostly good. Three points (two i mentioned above).
    • you forget to close the file you were reading if you return false
    • some really unneeded code at the end and I can't remember what the defined behavior is for that call since you already reached EOF to get out of the loop... but I don't think this is causing your memory errors or causing you to fail any tests.
    • what should happen if they call load after load without an unload? Or load, unload, load? which is the perfect segue to size
  • size() : looks good... only issue is you only ever increment it, never make use of it (and you really should), and it's incorrect after you unload a dictionary... what should size be after unload? what if you load, unload and load again?
  • unload(): doesn't look good. You return early currently, on the first pass of the for loop. Secondly your return value is missing a very simple check you could make that would ensure consistency in your program and it's missing an assignment you should be making.. Think about this for a moment. What should size be after you call unload? If it were me I'd be decrementing wordss every time I free'd something, and making sure it was zero at the end, or I'd be incrementing a counter on each free and verifying that counter equaled size and then resetting size to zero before returning true. I dont know what I would do to size if they didn't match, but I kinda like my first idea because of this, in that it would tell me how many items I created but didn't free if you decrement it for each free. It would help perform a unit test that dictionary loads the right amount of words ( size check after load ) and definately unloaded everything ( size check inside of and after unload ), assuming they both returned true. And if one of them returns false, it gives me a clue about what went wrong. Also see my notes about size, what should size be after unload succeeds?
3
  • thank you for pointing out me quitting early in Unload- im such an idiot! Thanks so much!! I fixed it but surprise surprise; check50 still shows memory error. I also added a condition at the end, where if sizee (equal to the value of wordss) equals 0, wordss is reset to 0 as well. It compiles so it returns true it seems. I dont think I understood the part where user could call load, unload, load? Shouldnt all be called once, as theres only one dictionary? Also implemented your notes :) Thank you very much for taking the time to help me!! I guess the memory issue is just out of reach for me
    – Rae
    Commented Jan 18 at 22:15
  • no lets keep going... open a new question again. and link it to this one. please run style 50 and then copy paste the code in to new question ... enclose the code in three '`' marks (backtick) then a new line paste the code then newline and three more backticks that will have it formatted properly... we will find the memory error.
    – UpAndAdam
    Commented Jan 19 at 16:40
  • my point about load/unload that you are not the one calling load and unload correct? the main program is.. imagine if the main program did those things. they are reasonable. what if it loaded two different files into your "dictionary" interface. what if they loaded one did some work and then wanted to unload it and load a different one to do some more work. none of these things are precluded by the instrucitons. where does it say that load will only ever get called once?
    – UpAndAdam
    Commented Jan 19 at 16:42

You must log in to answer this question.

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