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).