0

I am able to compile the program, but what happens when I run it is that all the words in the text file will be printed as MISSPELLED WORDS, and then I'll get a segmentation fault error message after the last word. I've looked through my code numerous times but I can't seem to find the problem. Any help would be greatly appreciated. Thank you.

For example, this is what I get when I run ./speller texts/ralph.txt:

MISSPELLED WORDS

When

I
grow

up

.... (body of the rest of the text)

Ralph

Wiggum

Segmentation fault

I have also run valgrind with help50, and when I do I get:

==8037== Stack overflow in thread 1: can't grow stack to 0xffe801ff8

==8037== 

==8037== Process terminating with default action of signal 11 (SIGSEGV)

==8037==  Access not within mapped region at address 0xFFE801FF8

==8037==    at 0x401714: clear (dictionary.c:192)

==8037==  If you believe this happened as a result of a stack

==8037==  overflow in your program's main thread (unlikely but

==8037==  possible), you can try to increase the size of the

==8037==  main thread stack using the --main-stacksize= flag.

==8037==  The main thread stack size used in this run was 8388608.

==8037== Stack overflow in thread 1: can't grow stack to 0xffe801ff0

==8037== 

==8037== Process terminating with default action of signal 11 (SIGSEGV)

==8037==  Access not within mapped region at address 0xFFE801FF0

==8037==    at 0x4A256B0: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-amd64-linux.so)

==8037==  If you believe this happened as a result of a stack

==8037==  overflow in your program's main thread (unlikely but

==8037==  possible), you can try to increase the size of the

==8037==  main thread stack using the --main-stacksize= flag.

==8037==  The main thread stack size used in this run was 8388608.

==8037== 

==8037== HEAP SUMMARY:

==8037==     in use at exit: 290,334,912 bytes in 1,296,138 blocks

==8037==   total heap usage: 1,296,140 allocs, 2 frees, 290,336,048 bytes allocated

==8037== 

==8037== LEAK SUMMARY:

==8037==    definitely lost: 0 bytes in 0 blocks

==8037==    indirectly lost: 0 bytes in 0 blocks

==8037==      possibly lost: 0 bytes in 0 blocks

==8037==    still reachable: 290,334,912 bytes in 1,296,138 blocks

==8037==         suppressed: 0 bytes in 0 blocks

==8037== Reachable blocks (those to which a pointer was found) are not shown.

==8037== To see them, rerun with: --leak-check=full --show-leak-kinds=all

==8037== 

==8037== For counts of detected and suppressed errors, rerun with: -v

==8037== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "dictionary.h"

typedef struct node
{
bool is_word;
struct node *children[27];
}
node;

//initialize pointers; root is pointer to the root node, current is pointer to node being analyzed at the moment
node *root;
node *current;

// counter for number of words in dictioary
int words = 0;

// character buffer to hold key while it is added to trie 
char word[45];

bool check(const char *word)
{
    // set current to point to root node
    current = root;

    // iterate through each character of word
    for (int i = 0; i < strlen(word); i++)
    {
        // if character is alphabetical
        if (word[i] != '\'')
        {
            // and is equal to NULL, return false
            if (current -> children[tolower(word[i]) - 'a'] == NULL)
                return false;

            // if not, move to next node
            else
                current = current -> children[tolower(word[i]) - 'a'];
        }

        // if character is an apostrophe
        else
        {
            // if pointer is equal to NULL, return false
            if (current -> children[26] == NULL)
                return false;

            else
                current = current -> children[26];
        }
    }

    // check to see if is_word is marked as true
    if (current -> is_word == true)
        return true;

    else
        return false;
}

bool load(const char *dictionary)
{
    // open dictionary, return 1 if unable to open
    FILE *dict = fopen(dictionary, "r");
    if (dict == NULL)
    {
        fprintf(stderr,"Could not open file\n");
        return false;
    }

    // allocate memory for the root node, return 2 if unable to allocate memory
    root = malloc(sizeof(node));
    if (root == NULL)
    {
        fprintf(stderr, "Could not allocate memory\n");
        return false;
    }

    // initialize is_word to false and array elements to NULL, have the current node pointer point to where the root node pointer is pointing
    else
    {
        root -> is_word = false;

        for (int i = 0; i < 27; i++)
            root -> children[i] = NULL;

        current = root;
    }    

    // read one word at a time from the dictionary and store word in array, until EOF is returned
    while (fscanf(dict, "%s", word) != EOF)
    {
        // iterate through each letter in word string
        for (int i = 0; i < strlen(word); i++)
        {
            // check to see if a new node needs to be created for i'th character of key
            if ((word[i] != '\'' && current -> children[tolower(word[i]) - 'a'] == NULL) || (word[i] == '\'' && current -> children[26] == NULL))
            {
                // if value is equal to a letter, allocate memory for it 
               if (word[i] != '\'')
                {
                    current -> children[tolower(word[i]) - 'a'] = malloc(sizeof(node));
                    if (current -> children[tolower(word[i]) - 'a'] == NULL)
                    {
                        fprintf(stderr, "Could not allocate memory");
                        return false;
                    }

                    // change current node pointer to point to the node that was just created
                    current = current -> children[tolower(word[i]) - 'a'];
                }

                // if the i'th element of the array is an apostrophe, allocate memory for it
                else
                {
                    current -> children[26] = malloc(sizeof(node));
                    if (current -> children[26] == NULL)
                    {
                        fprintf(stderr, "Could not allocate memory");
                        return false;
                    }

                    // change current node pointer to point to the node that was just created
                    current = current -> children[26];
                }    

                // after all the letters of the key have been added, change is_word to be true
                current -> is_word = false;

                // initialize array of pointers to NULL
                for (int j = 0; j < 27; j++)
                    current -> children[j] = NULL;
            }

            // if node already exists for i'th character of key, navigate through that node and change the value of the pointer for currrent
            else 
            {    
                if (word[i] != '\'')
                    current = current -> children[tolower(word[i]) - 'a'];

                else
                    current = current -> children[26];
            }    
        }

        // after the key has been inserted, change the value of is_word to true
        current -> is_word = true;

        // increase word count by 1
        words++;
    }

    fclose(dict);

    return true;
}  

unsigned int size(void)
{
    return words;
}

void clear(node *ptr)
{
    // iterate through each pointer array
    for (int i = 0; i < 27; i++)
    {
        //iterate through each pointer array in next node and so on, using recursive function
        if (ptr -> children[i] != NULL)
            clear(ptr -> children[i]);
    }

    free(ptr);  
}


bool unload(void)
{
    clear(root);
    return true;
}

1 Answer 1

1

The first thing that should get your attention, before valgrind, before help50, is this; Could not open file. What does the code do if the file is not found? return 1;. Review speller.c again. It calls load here bool loaded = load(dictionary);. Then it tests the return value of load here if (!loaded). Remembering that 1 evaluates to true and 0 evaluates to false, what happens next? speller continues on it's way because !loaded evaluates to false! So it's trying to check the text against no dictionary!

Suggest all the non-successful returns from load should be return false; to indicate the dictionary did not load properly, since the function returns a bool.

If you were using a custom dictionary when you called speller, check the file name. If you were using the small dictionary, it should be located in "dictionaries/small", so the call would be ./speller dictionaries/small textfile. If you were using no dictionary argument, then make sure your directory layout matches the distribution code.

4
  • Thanks for your suggestions! Changing FILE *dict = fopen("dictionary", "r"); to FILE *dict = fopen(dictionary, "r"); solved the issue of not being able to open the file. I changed all the non-successful 'returns` within the load function to be 'return false'.
    – Rayango
    Commented Jun 21, 2017 at 18:20
  • However, after doing all that, I still run into a segmentation fault, and I've edited my post to reflect the new error message. From valgrind, it seems that I'm having a problem with stack overflow due to my recursive function, but I am not sure why that is exactly.
    – Rayango
    Commented Jun 21, 2017 at 18:28
  • usually a missing return in the recursive function. See if this helps. Commented Jun 21, 2017 at 19:15
  • I changed some lines in my load and check function, and that solved the problem. I have no idea what it was exactly, but thank you for your help.
    – Rayango
    Commented Jun 22, 2017 at 22:11

You must log in to answer this question.

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