1

Here is my code:

// Implements a dictionary's functionality

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

#include "dictionary.h"

// Maximum length for a word
#define LENGTH 45

//define struct node
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
}
node;

//hash function
unsigned int hash(const char word[LENGTH + 1])
{
    int x = atoi(&word[0]);
    int y;
    if(isupper(word[0]))
    {
        y = (x + 13) % 26;
    }
    if(islower(word[0]))
    {
        y = (x + 7) % 26;
    }
    if(isalpha(word[0]) == 0)
    {
        return 0;
    }
    return y;
}

//make a hash table of linked lists
node* hash_table[26];

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

// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
    char word[LENGTH + 1];
    FILE *dictionary_file = fopen(dictionary, "r");
    if (dictionary_file == NULL)
    {
        unload ();
        return false;
    }

    while (fscanf(dictionary_file, "%s", word) != EOF)
    {
        //allocate memory for node
        node *new_node = malloc(sizeof(node));
        if (new_node == NULL)
        {
            unload();
            return false;
        }

        //insert node into linked list
        int index = hash(word);
        node *head = hash_table[hash(word)];

        //place word in node
        strcpy(new_node->word, word);

        //connect nodes
        new_node->next = head;
        hash_table[index] = new_node;
        }
    fclose(dictionary_file);
    return true;
}

// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
    int sum = 0;
    if (&load)
    {
        char word[LENGTH + 1];
        const char *dictionary;
        FILE *dictionary_file = fopen(dictionary, "r");
        while (fscanf(dictionary_file, "%s", word) != EOF)
        {
            sum++;
        }
    }
    return sum;
}

// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
    char word[LENGTH + 1];
    node *head = hash_table[hash(word)];
    node *cursor = head;

    while (cursor != NULL)
    {
        node *temp = cursor;
        cursor = cursor->next;
        free(temp);
    }

    if (cursor == NULL)
    {
        return true;
    }
    else
    {
       return false;
    }
}

When I run it through the GDB, I get this message that indicates a segmentation fault:

Program received signal SIGSEGV, Segmentation fault.
_int_malloc (av=0x7ffff728d760 <main_arena>, bytes=56) at malloc.c:3777
3777    malloc.c: No such file or directory.

Here is the GDB response when I type "where":

#0  _int_malloc (av=0x7ffff728d760 <main_arena>, bytes=56) at malloc.c:3777
#1  0x00007ffff6f4dae0 in __GI___libc_malloc (bytes=56) at malloc.c:2893
#2  0x0000000000422abf in load (dictionary=0x382e332d6e696168 <error: Cannot access memory at address 0x382e332d6e696168>)
    at dictionary.c:66
#3  0x722d72656c69706d in ?? ()
#4  0x61732f62696c2f74 in ?? ()
#5  0x5f72657a6974696e in ?? ()
#6  0x732f6e6f6d6d6f63 in ?? ()
#7  0x72657a6974696e61 in ?? ()
#8  0x632e7367616c665f in ?? ()
#9  0x6165720000000063 in ?? ()
#10 0x0063632e31720064 in ?? ()
#11 0x2828000000000000 in ?? ()
#12 0x5f76625f706d7421 in ?? ()
#13 0x287469427465672e in ?? ()
#14 0x6c00292929786469 in ?? ()
#15 0x6c6f6f742d6d766c in ?? ()

Does anyone have an idea of why the segmentation fault arises? When I run the program through valgrind, the system indicates that no memory has been leaked. Does anyone have an idea of how to fix this?

Franklin Estein
  • 21
  • 1
  • 1
  • 5
  • `&word[0]` can be written as just `word` – Barmar Jul 25 '18 at 22:44
  • 1
    `hash_table` is an array of pointers. Did you ever initialize them to point to nodes? – Barmar Jul 25 '18 at 22:46
  • @FranklinEstein Couple of issues: your `hash()` function could probably use a bit of a rewrite. As it is right now, its result will only ever depend on the first letter of the word. Secondly if you're inside GDB when it crashes, use the `bt` command to print a stack backtrace. Assuming your debug options to the compiler are set correctly, that'll tell you (A) where you crashed, and (B) how you got there. – dgnuff Jul 25 '18 at 23:14
  • `bool loaded = &load;` is legal, but it makes no sense. `&load` is the address of the `load` function (it's not called). That address is non-null, so converting it to `bool` simply yields `true`. – Keith Thompson Jul 25 '18 at 23:43
  • 2
    Please show us a complete program. The code currently in your question has no `#include` directives and no `main` function. Read this: [mcve] – Keith Thompson Jul 25 '18 at 23:44
  • 5
    Incidentally, the "No such file or directory" message is not relevant. It just means that your program crashed in the runtime library, and gdb was unable to see the library source code. It's the segmentation fault that's relevant -- and gdb should be able to tell you where in your own code it occurred. (Try the `where` command after you get that message.) – Keith Thompson Jul 25 '18 at 23:45
  • `if (loaded == true)` is more clearly written as `if (loaded)` – Keith Thompson Jul 25 '18 at 23:46
  • 2
    Crashes inside of malloc(), in my experience, generally point to memory corruption done on the buffers it allocates - things like writing before or after your allocated buffer. Make sure your array writes aren't going past the beginning/end of arrays. Assert that your hash is indeed returning a value in range... – Michael Dorgan Jul 25 '18 at 23:57
  • assert that length >= 26 too... – Michael Dorgan Jul 25 '18 at 23:59
  • @dgnuff The `load()` function never assigns anything to `hash_table[hash(new_node->word)]`. It assumes this is already populated with the head of a linked list. – Barmar Jul 26 '18 at 00:03
  • @Barmar You're right. It *reads* the hash table, but doesn't *write* it. – dgnuff Jul 26 '18 at 00:04
  • 1
    @dgnuff But I just realized that the hash table is a global variable, so it's initialized to all null pointers automatically. – Barmar Jul 26 '18 at 00:06
  • Yup. failure to populate the table is not going to cause UB because the OP is doing a good job of checking pointers for `NULL`. – dgnuff Jul 26 '18 at 00:08
  • Memory leaks don't cause segmentation faults. You probably have a buffer overflow somewhere. – Barmar Jul 26 '18 at 00:42
  • Read [Definitive list of common reasons for segmentation faults](https://stackoverflow.com/questions/33047452/definitive-list-of-common-reasons-for-segmentation-faults) – Barmar Jul 26 '18 at 00:43
  • regarding: `if (&load)` This will ALWAYS evaluate to `true`, probably not what you want – user3629249 Jul 26 '18 at 00:53
  • What have you done that main isn't in the backtrack? – Joshua Jul 26 '18 at 00:54
  • @user3629249 what should I do instead? – Franklin Estein Jul 26 '18 at 00:57
  • I don't know what you were trying to accomplish with that `if (&load)` statement, so I cannot advise you – user3629249 Jul 26 '18 at 01:09
  • @user3629249 I want the equivalent of if(load == true) – Franklin Estein Jul 26 '18 at 01:30
  • Understand, instead of `unsigned int hash(const char word[LENGTH + 1])`, it perfectly fine (and more readable) to simply use a pointer as the parameter, e.g. `unsigned int hash(const char *word)`, and similarly `int x = atoi(&word[0]);` is simply `int x = atoi(word);`. On access, an array is converted to a pointer to its first element. – David C. Rankin Jul 26 '18 at 02:36
  • `load` is the name of one of the functions in your posted code. So it will always have a value (its' address) so the condition will ALWAYS be true – user3629249 Jul 27 '18 at 06:02

2 Answers2

1

regarding:

const char *dictionary;
FILE *dictionary_file = fopen(dictionary, "r");

(probably the cause of the seg fault)

the pointer dictionary is not initialized before being passed to fopen()

so the code is trying to read from some 'random' location in memory for some 'random' number of bytes (until a NUL byte is encountered) to try to obtain the name of the file to be opened.

OT: when calling C library functions, like fopen(), always follow the call with a check for any errors.

When compiling, always enable the warnings, then fix those warnings. (for gcc, at a minimum use: -Wall -Wextra -Wconversion -pedantic -std=gnu11 )

user3629249
  • 16,402
  • 1
  • 16
  • 17
0

There are several problems in the load() function, but I'm not sure they're responsible for the segmentation fault.

  1. You're calling hash(new_node->word) before you copy from word into new_node->word. You should hash word instead.

  2. You're never actually putting the new node into the hash table. You're setting head to the new node after you link it to the old head, but the hash table still points to the old head.

Updated code:

// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
    char word[LENGTH + 1];
    FILE *dictionary_file = fopen(dictionary, "r");
    if (dictionary_file == NULL)
    {
        unload ();
        return false;
    }

    while (fscanf(dictionary_file, "%s", word) != EOF)
    {
        //allocate memory for node
        node *new_node = malloc(sizeof(node));
        if (new_node == NULL)
        {
            unload();
            return false;
        }

        //insert node into linked list
        int hashcode = hash(word);
        node *head = hash_table[hashcode];

        //place word in node
        strcpy(new_node->word, word);

        //connect nodes
        new_node->next = head;
        hash_table[hashcode] = new_node;
    }
    fclose(dictionary_file);
    return true;
}

I don't see anything in the code you posted that would cause a segmentation fault, so you probably have undefined behavior in some other code that you didn't include. Use a tool like valgrind to help find it.

Barmar
  • 741,623
  • 53
  • 500
  • 612