Home > front end >  Speller compiles, runs and passes all manual tests, but doesn't produce any check50 output
Speller compiles, runs and passes all manual tests, but doesn't produce any check50 output

Time:08-05

My code runs and produces the correct output for all tests done manually, but when I run it through check50, all I get is expected "MISSPELLED WOR...", not "". It shows no errors in valgrind; yet when I use debug50 and go to any function in dictionary.c, the debugger crashes with a segmentation fault, producing a file called Could not load source ./string/../sysdeps/x86_64/multiarch/strlen-evex.S': 'SourceRequest' not supported... But when I use the debugger with no breakpoints, it executes. Any help anyone can give would be super appreciated, thanks!

Here is the dictionary.c code:

// Implements a dictionary's functionality

#include <ctype.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.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];

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

// Hashes word to a number
unsigned int hash(const char *word)
{
    return tolower(word[0]) - 97;
}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    long int dictSize;
    for (unsigned int i = 0; i < N; i  )
    {
        table[i] = NULL;
    }
    FILE *file = fopen(dictionary, "r");
    if (file == NULL)
    {
        return false;
    }
    fseek(file, 0L, SEEK_END);
    dictSize = ftell(file);
    char *text = calloc(dictSize   1, sizeof(char));
    fseek(file, 0L, SEEK_SET);
    fread(text, sizeof(char), dictSize, file);
    text[dictSize] = '\n';
    for (long int i = 0, j = 0; i < dictSize   1; i  )
    {
        if (text[i] == '\n' || text[i] == '\0')
        {
            char *word = calloc(i - j   1, sizeof(char));
            if (word == NULL)
            {
                fclose(file);
                free(text);
                return false;
            }
            for (long k = 0; j <= i; k  , j  )
            {
                if (j == i)
                {
                    word[k] = '\0';
                }
                else
                {
                    word[k] = text[j];
                }
            }
            if (!(addToTable(word)))
            {
                free(word);
                fclose(file);
                free(text);
                return false;
            }
            j = i   1;
            free(word);
        }
    }
    fclose(file);
    free(text);
    return true;
}

// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    unsigned int count = 0;
    for (unsigned int i = 0; i < N; i  )
    {
        node *trav = table[i];
        while (trav != NULL)
        {
            count  ;
            trav = trav->next;
        }
    }
    return count;
}

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

bool addToTable(char *temp)
{
    if (table[hash(temp)] == NULL)
    {
        table[hash(temp)] = calloc(1, sizeof(node));
        if (table[hash(temp)] == NULL)
        {
            return false;
        }
        table[hash(temp)]->next = NULL;
        for (int i = 0; i < strlen(temp); i  )
        {
            table[hash(temp)]->word[i] = temp[i];
        }
        table[hash(temp)]->word[strlen(temp)] = '\0';
    }
    else
    {
        node *trav = table[hash(temp)];
        while (trav->next != NULL)
        {
            trav = trav->next;
        }
        trav->next = calloc(1, sizeof(node));
        if (trav->next == NULL)
        {
            return false;
        }
        trav->next->next = NULL;
        for (int i = 0; i < strlen(temp); i  )
        {
            trav->next->word[i] = temp[i];
        }
        trav->next->word[strlen(temp)] = '\0';
    }
    return true;
}

Here is my dictionary.h code:

// Declares a dictionary's functionality

#ifndef DICTIONARY_H
#define DICTIONARY_H

#include <stdbool.h>

// Maximum length for a word
// (e.g., pneumonoultramicroscopicsilicovolcanoconiosis)
#define LENGTH 45

// Prototypes
bool check(const char *word);
unsigned int hash(const char *word);
bool load(const char *dictionary);
unsigned int size(void);
bool unload(void);
bool addToTable(char *temp);

#endif // DICTIONARY_H

Here is the speller.c (main) code:

// Implements a spell-checker

#include <ctype.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/time.h>

#include "dictionary.h"

// Undefine any definitions
#undef calculate
#undef getrusage

// Default dictionary
#define DICTIONARY "dictionaries/large"

// Prototype
double calculate(const struct rusage *b, const struct rusage *a);

int main(int argc, char *argv[])
{
    // Check for correct number of args
    if (argc != 2 && argc != 3)
    {
        printf("Usage: ./speller [DICTIONARY] text\n");
        return 1;
    }

    // Structures for timing data
    struct rusage before, after;

    // Benchmarks
    double time_load = 0.0, time_check = 0.0, time_size = 0.0, time_unload = 0.0;

    // Determine dictionary to use
    char *dictionary = (argc == 3) ? argv[1] : DICTIONARY;

    // Load dictionary
    getrusage(RUSAGE_SELF, &before);
    bool loaded = load(dictionary);
    getrusage(RUSAGE_SELF, &after);

    // Exit if dictionary not loaded
    if (!loaded)
    {
        printf("Could not load %s.\n", dictionary);
        return 1;
    }

    // Calculate time to load dictionary
    time_load = calculate(&before, &after);

    // Try to open text
    char *text = (argc == 3) ? argv[2] : argv[1];
    FILE *file = fopen(text, "r");
    if (file == NULL)
    {
        printf("Could not open %s.\n", text);
        unload();
        return 1;
    }

    // Prepare to report misspellings
    printf("\nMISSPELLED WORDS\n\n");

    // Prepare to spell-check
    int index = 0, misspellings = 0, words = 0;
    char word[LENGTH   1];

    // Spell-check each word in text
    char c;
    while (fread(&c, sizeof(char), 1, file))
    {
        // Allow only alphabetical characters and apostrophes
        if (isalpha(c) || (c == '\'' && index > 0))
        {
            // Append character to word
            word[index] = c;
            index  ;

            // Ignore alphabetical strings too long to be words
            if (index > LENGTH)
            {
                // Consume remainder of alphabetical string
                while (fread(&c, sizeof(char), 1, file) && isalpha(c));

                // Prepare for new word
                index = 0;
            }
        }

        // Ignore words with numbers (like MS Word can)
        else if (isdigit(c))
        {
            // Consume remainder of alphanumeric string
            while (fread(&c, sizeof(char), 1, file) && isalnum(c));

            // Prepare for new word
            index = 0;
        }

        // We must have found a whole word
        else if (index > 0)
        {
            // Terminate current word
            word[index] = '\0';

            // Update counter
            words  ;

            // Check word's spelling
            getrusage(RUSAGE_SELF, &before);
            bool misspelled = !check(word);
            getrusage(RUSAGE_SELF, &after);

            // Update benchmark
            time_check  = calculate(&before, &after);

            // Print word if misspelled
            if (misspelled)
            {
                printf("%s\n", word);
                misspellings  ;
            }

            // Prepare for next word
            index = 0;
        }
    }

    // Check whether there was an error
    if (ferror(file))
    {
        fclose(file);
        printf("Error reading %s.\n", text);
        unload();
        return 1;
    }

    // Close text
    fclose(file);

    // Determine dictionary's size
    getrusage(RUSAGE_SELF, &before);
    unsigned int n = size();
    getrusage(RUSAGE_SELF, &after);

    // Calculate time to determine dictionary's size
    time_size = calculate(&before, &after);

    // Unload dictionary
    getrusage(RUSAGE_SELF, &before);
    bool unloaded = unload();
    getrusage(RUSAGE_SELF, &after);

    // Abort if dictionary not unloaded
    if (!unloaded)
    {
        printf("Could not unload %s.\n", dictionary);
        return 1;
    }

    // Calculate time to unload dictionary
    time_unload = calculate(&before, &after);

    // Report benchmarks
    printf("\nWORDS MISSPELLED:     %d\n", misspellings);
    printf("WORDS IN DICTIONARY:  %d\n", n);
    printf("WORDS IN TEXT:        %d\n", words);
    printf("TIME IN load:         %.2f\n", time_load);
    printf("TIME IN check:        %.2f\n", time_check);
    printf("TIME IN size:         %.2f\n", time_size);
    printf("TIME IN unload:       %.2f\n", time_unload);
    printf("TIME IN TOTAL:        %.2f\n\n",
           time_load   time_check   time_size   time_unload);

    // Success
    return 0;
}

// Returns number of seconds between b and a
double calculate(const struct rusage *b, const struct rusage *a)
{
    if (b == NULL || a == NULL)
    {
        return 0.0;
    }
    else
    {
        return ((((a->ru_utime.tv_sec * 1000000   a->ru_utime.tv_usec) -
                  (b->ru_utime.tv_sec * 1000000   b->ru_utime.tv_usec))  
                 ((a->ru_stime.tv_sec * 1000000   a->ru_stime.tv_usec) -
                  (b->ru_stime.tv_sec * 1000000   b->ru_stime.tv_usec)))
                / 1000000.0);
    }
}

Here is my makefile (clang):

speller:
    clang -ggdb3 -gdwarf-4 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-gnu-folding-constant -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -c -o speller.o speller.c
    clang -ggdb3 -gdwarf-4 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-gnu-folding-constant -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -c -o dictionary.o dictionary.c
    clang -ggdb3 -gdwarf-4 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-gnu-folding-constant -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -o speller speller.o dictionary.o -lm

CodePudding user response:

As noted in a comment, one compilation problem arose because at file scope, you have:

const unsigned int N = 26;
node *table[N];

GCC legitimately complains about a variably modified array table at file scope. C is not C ; what would be valid in C is not valid in C. N is a constant integer; it is not an integer constant and you have to use an integer constant for the dimension of a statically allocated array. Use an enum — that's an integer constant:

enum { N = 26 };

I fixed that and also a few cases of comparing signed and unsigned integers:

-        for (int i = 0; i < strlen(temp); i  )
         for (size_t i = 0; i < strlen(temp); i  )

In my testing, the code crashed in addToTable() at the end of the word list when an empty string was passed to the function. Part of the cause of the crash was that the hash() function returned -97 (where the 97 should be written 'a').

A simple fix for the problem is in load(), replacing:

         if (!(addToTable(word)))

with:

         if (word[0] != '\0' && !(addToTable(word)))

With that in place, the code worked correctly on a dictionary of 64 words, and also on /usr/share/dict/words (which contained 235,886 words).

  • Related