cs50 speller keeps prompting free(): double free detected in tcache 2 - c

I have been working on this problem set for quite a time and the code seems to be wrong but I couldn't find the solution. I have been comparing my code and other people's code but I still don't know where I got wrong. Really appreciate all your help if you can provide me with some ways to solve this problem. It keeps prompting me free(): double free detected in tcache 2 but I can't seem to find my mistake.
// Implements a dictionary's functionality
#include <stdbool.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 50;
// Hash table
node *table[N];
//word count
int count = 0;
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// TODO
bool found = false;
node *current = table[hash(word)];
while (current != NULL)
{
if (strcasecmp(current -> word, word) == 0)
{
found = true;
}
else if(current -> next != NULL)
{
current = current -> next;
}
else
{
return false;
}
}
return found;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// TODO
unsigned long hash = 5381;
int c;
while ((c = toupper(*word++)))
{
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
return hash % N;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// TODO
FILE *infile = fopen(dictionary, "r");
if (infile == NULL)
{
return false;
}
char buffer[LENGTH+1];
while (fscanf(infile, "%s", buffer) != EOF)
{
node *n = malloc(sizeof(node));
strcpy(n -> word, buffer);
n -> next = table[hash(buffer)];
table[hash(buffer)] = n;
count++;
free(n);
}
fclose(infile);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// TODO
return count;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
// TODO
int num = count;
for (int i = 0; i < N ; i++)
{
node *current = table[i];
while (current != NULL)
{
node *temp = current;
current = current -> next;
free(temp);
num--;
}
}
if (num == 0)
{
return true;
}
else
{
return false;
}
}

The calls of free in this while loop
while (fscanf(infile, "%s", buffer) != EOF)
{
node *n = malloc(sizeof(node));
strcpy(n -> word, buffer);
n -> next = table[hash(buffer)];
table[hash(buffer)] = n;
count++;
free(n);
}
does not make a sense. You deleted at once (an object of the type node using the pointer n) what you was trying to add to the table (a valid address to an allocated object of the type node). As a result the element of the table at the position hash(buffer) that is set like
table[hash(buffer)] = n;
has an invalid value because it is the address of the already deleted node in this statement
free(n);
So in the function unload this invalid address will be again used to free already freed memory within the function load.
Pay attention to that you did not allocate memory as you wrote in a comment "for node n". n is just a pointer to the allocated unnamed object of the type node. So you are not freeing the pointer n itself in this statement
free(n);
You are freeing the allocated object of the type node using the pointer n. Thus all pointers that pointed to the allocated object of the type node become invalid.

Related

CS50 PSET 5 (Speller): How do I get my dictionary to correctly check one letter words?

I explored the possibility that i'm skipping the last node in a linked list, but I can't find the error.
#include <ctype.h>
#include <stdbool.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;
unsigned int COUNT = 0;
// TODO: Choose number of buckets in hash table
const unsigned int N = 7480;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
int checkhash;
char lowerword[LENGTH + 1];
// convert word to lower case
for (int i = 0; i <= strlen(word); i++)
{
lowerword[i] = tolower(word[i]);
}
//get hash number
checkhash = hash(lowerword);
//check if it's in dictionary
node *trav = table[checkhash]; // traversal pointer
while (trav != NULL)
{
if (!strcasecmp(word, trav->word))
{
return true;
}
trav = trav->next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
unsigned int hash_num = 0;
for (int i = 0; i < strlen(word); i += 3)
{
hash_num += word[i] * word[2];
hash_num += i + 3 * i;
}
hash_num += 3435;
return hash_num % N;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// TODO
// • Open dictionary file
FILE *dp = fopen(dictionary, "r");
// • Read strings from file one at a time
if (dp == NULL)
{
printf("Error: Could not open dictionary\n");
return false;
}
// declaring needed variables
char word[LENGTH + 1];
while (fscanf(dp, "%s", word) != EOF) // call fscanf untill EOF is returned
{
// • Create a new node for each word
// • Use malloc
// • Remember to check if return value is NULL
// • Copy word into node using strcpy
node *newnode = malloc(sizeof(node));
if (newnode == NULL)
{
unload();
printf("There is insufficient memory to load dictionary\n");
return false;
}
strcpy(newnode->word, word);
// • Hash word to obtain a hash value
// • Use the hash function
// • Function takes a string and returns an index
int index = hash(word);
// • Insert node into hash table at that location
if (table[index] == NULL)
{
table[index] = newnode;
newnode->next = NULL;
}
else
{
newnode->next = table[index];
table[index] = newnode;
}
COUNT++;
}
fclose(dp);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
return COUNT;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
node *trav; // traversal pointers
node *tmp;
for (int i = 0; i < N; i++)
{
trav = table[i]; //next pointer in array
// recursively free list
while (trav != NULL)
{
tmp = trav;
trav = trav->next;
free(tmp);
}
}
// TODO
return true;
}
I have tried readjusting the check function for the last 2 hours with no luck. I don't think it is the hash function as I've parsed through it for a while without finding any logical errors. I'm nearly certain that the error will be found within check(). I can't wait to smack my forehead over a newbie error.
Here is the feedback I get from check50:
MISSPELLED WORDS
WORDS MISSPELLED: 0
WORDS IN DICTIONARY: 1
WORDS IN TEXT: 1
Actual Output:
MISSPELLED WORDS
a
WORDS MISSPELLED: 1
WORDS IN DICTIONARY: 1
WORDS IN TEXT: 1

CS50 Pset5 Speller, Check50 says that my code is all correct, but its not right when I test my code

I have been working on the Speller assignment in pset5, and when I run the check50 command, everything seems to be fine.
:) dictionary.c exists
:) speller compiles
:) handles most basic words properly
:) handles min length (1-char) words
:) handles max length (45-char) words
:) handles words with apostrophes properly
:) spell-checking is case-insensitive
:) handles substrings properly
:) program is free of memory errors
But when I start testing the program myself by running "./speller texts/lalaland.txt " This happens
ps5/speller/ $ ./speller texts/lalaland.txt
Segmentation fault (core dumped)
Here is my code, code anyone help spot what is wrong with this.
#include <ctype.h>
#include <stdbool.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 = 676;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
int number = hash(word);
node *current = table[number];
while (current != NULL)
{
if (strcasecmp(word, current -> word) == 0)
{
return true;
}
current = current -> next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// TODO: Improve this hash function
int a = toupper(word[0]) - 'A';
int b = toupper(word[1]) - 'A';
return (a * b);
}
int counter;
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
//Open the file and check if it actually exists
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
printf("Unable to open file\n");
return false;
}
for (int i = 0; i < N; i++)
{
table [i] = malloc(sizeof(node));
table [i]-> next = NULL;
for (int u = 0; u < 48 ; u++)
{
table [i]-> word[u] = '0';
}
}
char buffer[LENGTH + 1];
//Read all the individual strings from the file
while (fscanf(file, "%s", buffer) != EOF)
{
//Create nodes and copy the words into them
node *current_node = malloc(sizeof(node));
if (current_node == NULL)
{
return false;
}
strcpy(current_node -> word, buffer);
int hashnumber = hash(buffer);
node *pointer = table[hashnumber];
if (pointer == NULL)
{
table[hashnumber] = current_node;
counter++;
}
else
{
current_node -> next = table[hashnumber];
table[hashnumber] = current_node;
counter++;
}
}
fclose(file);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
return counter;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
for (int i = 0; i < 676; i++)
{
node *current = table[i];
while (current != NULL)
{
node *temp = current;
current = current -> next;
free(temp);
}
}
return true;
}
Try this:
node *current_node = malloc(sizeof(node));
if (current_node == NULL)
{
return false;
}
current_node->next = NULL; // Missing, and presumed NULL..
strcpy(current_node->word, buffer);
It seemed like once I initialized the variable as NULL and made changes to the hash function. I checked through the other files and it worked now. Thanks!

When I run the program, it shows "killed". How do I overcome this issue?

This is regarding pset5 speller, and below is my code. When I run it, it shows "killed" in my terminal. Moreover, when I try to debug it or try valgrind, the program just stops and exits. How do I overcome this issue?
#include <ctype.h>
#include <stdbool.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 = 150000;
// Hash table
node *table[N];
//Declare variables here so that they can be used in the different functions below
unsigned int HASH_INDEX;
unsigned int NO_OF_WORDS = 0;
node *CURSOR;
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// TODO
HASH_INDEX = hash(word);
CURSOR = table[HASH_INDEX];
do
{
if (strcasecmp(CURSOR->word, word) == 0)
{
return true;
}
else
{
CURSOR = CURSOR->next;
}
}
while (CURSOR != NULL);
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// TODO: Improve this hash function
// close address, each bucket in the hash table is a pointer
unsigned int hash_value = 0;
for (int i = 0; i < strlen(word); i++)
{
int c = tolower(word[i]);
hash_value = hash_value + c;
}
hash_value = hash_value % 31;
return hash_value;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// TODO
// open dictionary file
//use fopen
//remember to check if return value is NULL, to make sure we can successfully open up the file. If can't open then return false
FILE *d = fopen(dictionary, "r");
if (d == NULL)
{
return false;
}
// read strings from file one at a time (one word in the dictionary at a time)
//fscanf(file, "%s", word) --> %s means want to read in a string, word is char array a place we reading the word into.
//need to keep looping this fscanf for every word in the dictionary until fscanf return EOF (once it reaches the end of file)
char word[LENGTH + 1];
while (fscanf(d, "%s", word) != EOF)
{
// create a new node for each word, a node that contains a value and the next pointer (this helps to store each word into the hash table)
//use malloc to store a new node
//remember to check if return value is NULL --> check whether malloc returns NULL or not becos if malloc does not have enough memory it will return NULL and our load function should return false
//copy word into node using strcpy --> it takes a string and copy it from one location into another location
node *w = malloc(sizeof(node));
if (w == NULL)
{
return false;
}
else
{
strcpy(w->word, word);
w->next = NULL;
// hash word using the hash function to obtain a hash value
//use the hash function defined in dictionary.c --> function takes a string and returns an index (to determine which index into the hash table we should use when inserting this node)
HASH_INDEX = hash(word);
// lastly, take each of those words and insert that node into the hash table at the location given by hash functon
//recall that hash table is an array of linked lists
//be sure to set pointers in the correct order --> point to the 2nd node first then shift the head to point to the new node
if (table[HASH_INDEX] == NULL)
{
table[HASH_INDEX] = w;
}
w->next = table[HASH_INDEX];
table[HASH_INDEX] = w;
NO_OF_WORDS++;
}
}
fclose(d);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// TODO
return NO_OF_WORDS;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
// TODO
for (int i = 0; i < N; i++)
{
CURSOR = table[i];
while (CURSOR != NULL)
{
node *tmp = CURSOR;
CURSOR = CURSOR->next;
free(tmp);
}
}
return true;
}
Would really appreciate it if someone could help me with this! Thank you in advance!

Segmentation fault in CS50 Speller. Why?

I am working on the CS50 pset5 Speller, and I keep getting a segmentation fault error. Debug50 suggests the problem is the line n->next = table[index]; in the implementation of the load function, line 110. I tried to revise but I can´t figure out why it would give error. Here below my code, can anyone please help me?
// Implements a dictionary's functionality
#include <stdbool.h>
#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node {
char word[LENGTH + 1];
struct node *next;
} node;
// Number of buckets in hash table
const unsigned int N = 150000;
// Nodes counter
int nodes_counter = 0;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// TODO
int hash_value = hash(word);
node *cursor = malloc(sizeof(node));
if (cursor != NULL)
{
cursor = table[hash_value];
}
if (strcasecmp(cursor->word, word) == 0) // If word is first item in linked list
{
return 0;
}
else // Iterate over the list by moving the cursor
{
while (cursor->next != NULL)
{
if (strcasecmp(cursor->word, word) == 0) // If word is found
{
return 0;
}
else
{
cursor = cursor->next;
}
}
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// Adaptation of FNV function, source https://www.programmingalgorithms.com/algorithm/fnv-hash/c/
const unsigned int fnv_prime = 0x811C9DC5;
unsigned int hash = 0;
unsigned int i = 0;
for (i = 0; i < strlen(word); i++)
{
hash *= fnv_prime;
hash ^= (*word);
}
return hash;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// Open Dictionary File (argv[1] or dictionary?)
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
printf("Could not open file\n");
return 1;
}
// Read until end of file word by word (store word to read in word = (part of node)?)
char word[LENGTH + 1];
while(fscanf(file, "%s", word) != EOF)
{
// For each word, create a new node
node *n = malloc(sizeof(node));
if (n != NULL)
{
strcpy(n->word, word);
//Omitted to avoid segmentation fault n->next = NULL;
nodes_counter++;
}
else
{
return 2;
}
// Call hash function (input: word --> output: int)
int index = hash(word);
// Insert Node into Hash Table
n->next = table[index];
table[index] = n;
}
return false;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// Return number of nodes created in Load
if (nodes_counter > 0)
{
return nodes_counter;
}
return 0;
}
// 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->next != NULL)
{
node *tmp = cursor;
cursor = cursor->next;
free(tmp);
}
}
return false;
}
There are multiple problems in your code:
node *table[N]; cannot be only be defined as a global object if N is a constant expression. N is defined as a const unsigned int, but N is not a constant expression in C (albeit it is in C++). Your program compiles only because the compiler accepts this as a non portable extension. Either use a macro or an enum.
you overwrite cursor as soon as it is allocated in check(). There is no need to allocate a node in this function.
the hash() function should produce the same hash for words that differ only in case.
the hash() function only uses the first letter in word.
the hash() function can return a hash value >= N.
fscanf(file, "%s", word) should be protected agains buffer overflow.
you do not check if cursor is non null before dereferencing it in unload()
Here is a modified version:
// Implements a dictionary's functionality
#include <ctype.h>
#include <stdbool.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;
// Number of buckets in hash table
enum { N = 150000 };
// Nodes counter
int nodes_counter = 0;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word) {
int hash_value = hash(word);
// Iterate over the list by moving the cursor
for (node *cursor = table[hash_value]; cursor; cursor = cursor->next) {
if (strcasecmp(cursor->word, word) == 0) {
// If word is found
return true;
}
}
// If word is not found
return false;
}
// Hashes word to a number
unsigned int hash(const char *word) {
// Adaptation of FNV function, source https://www.programmingalgorithms.com/algorithm/fnv-hash/c/
unsigned int fnv_prime = 0x811C9DC5;
unsigned int hash = 0;
for (unsigned int i = 0; word[i] != '\0'; i++) {
hash *= fnv_prime;
hash ^= toupper((unsigned char)word[i]);
}
return hash % N;
}
// Loads dictionary into memory, returning true if successful, else a negative error number
int load(const char *dictionary) {
// Open Dictionary File (argv[1] or dictionary?)
FILE *file = fopen(dictionary, "r");
if (file == NULL) {
printf("Could not open file\n");
return -1;
}
// Read until end of file word by word (store word to read in word = (part of node)?)
char word[LENGTH + 1];
char format[10];
// construct the conversion specifier to limit the word size
// read by fscanf()
snprintf(format, sizeof format, "%%%ds", LENGTH);
while (fscanf(file, format, word) == 1) {
// For each word, create a new node
node *n = malloc(sizeof(node));
if (n == NULL) {
fclose(file);
return -2;
}
strcpy(n->word, word);
n->next = NULL;
nodes_counter++;
// Call hash function (input: word --> output: int)
int index = hash(word);
// Insert Node into Hash Table
n->next = table[index];
table[index] = n;
}
fclose(file);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void) {
// Return number of nodes created in Load
return nodes_counter;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void) {
for (int i = 0; i < N; i++) {
node *cursor = table[i];
table[i] = NULL;
while (cursor != NULL) {
node *tmp = cursor;
cursor = cursor->next;
free(tmp);
}
}
return true;
}

Loading a trie data structure in C- pset5 cs50

I am having trouble loading data into my trie structure. I keep getting a sgemenation fault. Does this have to do with my malloc? Anyone see any issues?
Thanks
/**
* dictionary.c
*
* Computer Science 50
* Problem Set 5
*
* Implements a dictionary's functionality.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "dictionary.h"
#define ASCII_OFFSET 97;
/** Node of the data structure used to store the dictionary key words
* Data Structures Type is Tries
* Includes a bool to indicate if the current node is the end of a word
* A pointer to the nodes child node
*/
typedef struct node
{
bool is_word;
struct node* children[27];
}
node;
node* rootNode;
node* nextNode;
//Variable to track the size of the dictinoary
int wordCount = 0;
/**
* Returns true if word is in dictionary else false.
*/
bool check(const char* word)
{
//Get words length
int wordLength = strlen(word);
for(int i = 0; i < wordLength; i++)
{
//taking the character we need to check
int charToCheck = tolower(word[i]) - ASCII_OFFSET;
//Checking to see if the char exists in the data strucutre, Trie;
if(nextNode->children[charToCheck] == NULL)
{
return false;
}
//Advance the next node down the trie
nextNode = nextNode->children[charToCheck];
}
nextNode = rootNode;
//Return what is_word return
return nextNode->is_word;
}
/**
* Loads dictionary into memory. Returns true if successful else false.
*/
bool load(const char* dictionary)
{
//Open dict.. file to read
FILE* file = fopen(dictionary,"r");
//Check if the dict.. exsists!
if(file == NULL)
{
return false;
}
//Creating the first node in our data strucutre
rootNode = malloc(sizeof(node));
nextNode = rootNode;
//Get character to store
int character = fgetc(file) - ASCII_OFFSET;
//Go through the dict... file
while(character != EOF)
{
//Go through each word in the file
while(character != '\n')
{
//Add into our data structure
if(nextNode->children[character] == NULL)
{
//Create memory inorder to insert the next node
nextNode->children[character] = malloc(sizeof(node));
nextNode = nextNode->children[character];
}else {
nextNode = nextNode->children[character];
}
//advance character to next
character = fgetc(file) - ASCII_OFFSET;
}
//advance character to next word
character = fgetc(file) - ASCII_OFFSET;
//Set the last node loaded to is_word to track the end of each word
nextNode->is_word = true;
wordCount++;
nextNode = rootNode;
}
fclose(file);
return true;
}
/**
* Returns number of words in dictionary if loaded else 0 if not yet loaded.
*/
unsigned int size(void)
{
return wordCount;
}
/**
* Unloads dictionary from memory. Returns true if successful else false.
*/
bool unload(void)
{
for(int i = 0; i < 26; i++)
{
if(nextNode->children[i] != NULL)
{
nextNode = nextNode->children[i];
unload();
}
}
free(nextNode);
return true;
}
You don't check that a char is in range before using it as index in the array. Any character outside the range a-z will cause buffer overflow.
You compare the character to known character constants after you have subtracted 97 from it.
You need to initialize the memory returned from malloc by assigning NULL to all array elements and set is_word to false.
Just took a short glance at the code, so I may have missed additional bugs.

Resources