I just finished pset5 of cs50, and one of functions is meant to load content of a dictionary into a hash table. Inside the loop in said function i have to malloc memory for a node that i will later assign to node in the hash table.
When i tried freeing node n after each loop iteration my function wouldn't work.
When i don't free it it does work and more confusingly it also passes valgrind check and cs50's check50 for memory leaks.
My questions are :
how would i free 'node n' to allow my function to still work?
Why doesn't valgrind detect any memory leaks when i don't free 'n' ? Is it example of undefined behavior ?
How does malloc in a loop work, does it allocate new chunk of memory each time or does it overwrite previous chunk of memory ?
Any answers would be greatly appreciated.
Here is the code :
bool load(const char *dictionary)
{
//Setting counter to determine wheather node comes second in linked list or not.
int counter = 0;
//declaring string array to store words from dictionary
char word1[LENGTH +1];
FILE *dic = fopen(dictionary, "r");
if(dic == NULL)
{
return false;
}
//Loop loading words from dictionary to hash table
while(fscanf(dic, "%s", word1) != EOF )
{
node *n = malloc(sizeof(node));
if (n == NULL)
{
return false;
free(n);
}
int i = hash(word1);
//Storing word in temporary node
strcpy(n->word, word1);
n->next = NULL;
//Three different conditions(first node of[i], second node of[i], and after second node of[i])
if(table[i] == NULL)
{
table[i] = n;
counter++;
counter2++;
}
else if (counter == 1)
{
table[i]->next = n;
counter = 0;
counter2++;
}
else
{
n->next = table[i];
table[i] = n;
counter2++;
}
}
fclose(dic);
return true;
You don't free memory in load. You free it in unload. That was the whole point.
If valgrind doesn't detect memory leaks, then presumably you have a working unload function. Why would it be undefined behaviour?
It will allocate new memory every time. This wouldn't work if it didn't.
Related
Using the debugger, the linked list seems to be successfully created inside the function, but it doesn't get updated "outside" in main. I don't know why it isn't updating since I'm using addresses and dynamic memory allocation, which if I'm not mistaken, doesn't get "cleared" once the function is exited.
int populate(node* list)
{
node* temp = NULL;
while(1)
{
printf("insert word: ");
char* word = getstring();
if(strcmp(word, "stop") == 0)
{
break;
}
//create a node
node* n = malloc(sizeof(node));
if(n == NULL)
{
return 1;
}
//put stuff in node
n->word = word;
n->next = NULL;
if (list == NULL) //first case only
{
list = n;
temp = n;
}
else
{
//set previous next to current node
temp->next = n;
//set pointer to current node
temp = temp->next;
}
}
}
int main()
{
node* list = NULL;
while(1)
{
printf("insert command: ");
char* word = getstring();
if (strcmp(word, "stop") == 0)
{
break;
}
else if (strcmp(word, "add") == 0)
{
populate(list);
}
else if (strcmp(word, "read") == 0)
{
readList(list);
}
}
}
Also, after my code runs, is the memory I've allocated automatically freed? Or am I gobbling up small chunks of my computers memory every time I test my program. (I'm using Xcode)
You need to pass the pointer node* list as a double pointer (pointer to pointer) instead of a pointer:
int populate(node** list)
{
This is because C language has value semantics. Everything is passed by value. So when you pass the list to populate(), you create a copy of the original pointer. They are both pointing to the same memory, but changes to one of the pointer will not be reflected in the other. This is why your list never gets updated.
Everything else will remain mostly the same. When calling the populate function you need to pass the address of the list:
populate(&list);
And in the populate() function, every occurrence of list will become *list since you need to de-reference it to get the original pointer.
I am currently working on pset5 from cs50.
My entire program compiles successfully but stops in the middle of the function called load when program is executed.
Below is my load function, and you can see the comment where it gave me a segmentation fault error.
If you can help me with figuring out how I should approach my error, please do let me know.
I understand that segmentation fault is caused when the program attempts to access a memory that does not belong to it.
However, I have allocated memory and checked whether there was enough memory to continue on the program.
I will provide comments to highlight what my code does.
// In another header file, I have defined 'LENGTH'
// Maximum length for a word
// (e.g., pneumonoultramicroscopicsilicovolcanoconiosis)
#define LENGTH 45
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Hash table
// I have initialized the array of `node` pointer to point `NULL`
node *table[N] = {NULL};
unsigned int word_counter = 0;
bool load(const char *dictionary)
{
// Open file, and if cannot open, return false
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
return false;
}
// read string in the file into array of character, `word` until reaching end of the file
char word[LENGTH + 1];
while (fscanf(file, "%s", word) != EOF)
{
// keep track of how many word exists in the file, for later use (not in this function)
word_counter += 1;
// allocated memory for struct type `node`, if not enough memory found, return false
node *n = (node*)malloc(sizeof(node));
if (n == NULL)
{
return false;
}
// assign index by hashing (hash function will not be posted in this question though.)
unsigned int index = hash(&word[0]);
// copy the word from file, into word field of struct type `node`
strncpy(n->word, word, sizeof(word));
// Access the node pointer in this index from array(table), and check is its `next` field points to NULL or not.
// If it is pointing to NULL, that means there is no word stored in this index of the bucket
if (table[index]->next == NULL) // THIS IS WHERE PROGRAM GIVES 'segmentation fault' !!!! :(
{
table[index]->next = n;
}
else
{
n->next = table[index];
table[index]->next = n;
}
}
return true;
}
You define ant initialize the hash table as:
node *table[N] = {NULL};
That means you have an array of null-pointers.
When you insert the first value in the table, then table[index] (for any valid index) will be a null pointer. That means table[index]->next attempt to dereference this null pointer and you will have undefined behavior.
You need to check for a null pointers first:
if (table[index] == NULL)
{
n->next = NULL;
}
else
{
n->next = table[index];
}
table[index] = n;
I keep getting segfault for my load function.
bool load(const char *dictionary)
{
//create a trie data type
typedef struct node
{
bool is_word;
struct node *children[27]; //this is a pointer too!
}node;
//create a pointer to the root of the trie and never move this (use traversal *)
node *root = malloc(sizeof(node));
for(int i=0; i<27; i++)
{
//NULL point all indexes of root -> children
root -> children[i] = NULL;
}
FILE *dptr = fopen(dictionary, "r");
if(dptr == NULL)
{
printf("Could not open dictionary\n");
return false;
}
char *c = NULL;
//scan the file char by char until end and store it in c
while(fscanf(dptr,"%s",c) != EOF)
{
//in the beginning of every word, make a traversal pointer copy of root so we can always refer back to root
node *trav = root;
//repeat for every word
while ((*c) != '\0')
{
//convert char into array index
int alpha = (tolower(*c) - 97);
//if array element is pointing to NULL, i.e. it hasn't been open yet,
if(trav -> children[alpha] == NULL)
{
//then create a new node and point it with the previous pointer.
node *next_node = malloc(sizeof(node));
trav -> children[alpha] = next_node;
//quit if malloc returns null
if(next_node == NULL)
{
printf("Could not open dictionary");
return false;
}
}
else if (trav -> children[alpha] != NULL)
{
//if an already existing path, just go to it
trav = trav -> children[alpha];
}
}
//a word is loaded.
trav -> is_word = true;
}
//success
free(root);
return true;
}
I checked whether I properly pointed new pointers to NULL during initialization. I have three types of nodes: root, traversal (for moving), and next_node. (i.) Am I allowed to null point the nodes before mallocing them? (ii.) Also, how do I free 'next_node' if that node is initialized and malloced inside an if statement? node *next_node = malloc(sizeof(node)); (iii.) If I want to set the nodes as global variables, which ones should be global? (iv.) Lastly, where do I set global variables: inside the main of speller.c, outside its main, or somewhere else? That's alot of questions, so you don't have to answer all of them, but it would be nice if you could answer the answered ones! Please point out any other peculiarities in my code. There should be plenty. I will accept most answers.
The cause of segmentation fault is the pointer "c" which you have not allocated memory.
Also, in your program -
//scan the file char by char until end and store it in c
while(fscanf(dptr,"%s",c) != EOF)
Once you allocate memory to pointer c, c will hold the word read from file dictionary.
Below in your code, you are checking for '\0' character-
while ((*c) != '\0')
{
But you are not moving the c pointer to point to next character in the string read because of which this code will end up executing infinite while loop.
May you can try something like this-
char *tmp;
tmp = c;
while ((*tmp) != '\0')
{
......
......
//Below in the loop at appropriate place
tmp++;
}
Im looking for a faster method of freeing up memory allocated to a hash table in my code. What I've written below works but I want to know if there is another method that would accomplish the task faster.
bool unload(void)
{
if (load)
{
node* temp;
node* crawler;
for(int n = 0; n < TABLESIZE; n++)
{
if (hashtable[n] != NULL)
{
// If only 1 node free it
crawler = hashtable[n];
while (crawler != NULL)
{
temp = crawler->next;
free(crawler);
crawler = temp;
}
// free last node in list
temp = crawler;
free(temp);
}
}
return true;
}
return false;
}
while (crawler != NULL)
{
…
}
// free last node in list
temp = crawler;
free(temp);
When the loop exits, crawler is equal to NULL. Therefore, while it is perfectly acceptable to pass it to free(), you can save some time by not doing so.
I'm trying to implement a dictionary of words using a hash table, so I need to have it global, and in one of my header files I declare it
extern node** dictionary;
Where node is
typedef struct node
{
char* word;
struct node* next;
} node;
Then in another file in which functions are defined I include the header which has the dictionary declaration, and also I add at the top
node** dictionary;
Then in the function which actually loads the dictionary I first allocate memory for the linked lists which will make the hash table
bool load(const char* dict_file)
{
dictionary = malloc(sizeof(node*) * LISTS);
FILE* dict = fopen(dict_file, "r");
if(dict == NULL)
return false;
char buffer[MAX_LEN + 2];
size_dict = 0;
while(fgets(buffer, MAX_LEN + 2, dict) != NULL)
{
node* new_node = malloc(sizeof(node));
int len = strlen(buffer);
new_node->word = malloc(sizeof(char) * (len));
//avoid \n
for(int i = 0; i < len - 1; i++)
new_node->word[i] = buffer[i];
new_node->word[len - 1] = '\0';
new_node->next = NULL;
int index = hash(buffer);
new_node->next = dictionary[index];
dictionary[index] = new_node;
size_dict++;
}
if (ferror(dict))
{
fclose(dict);
return false;
}
fclose(dict);
return true;
}
So the program works fine, I then free all the allocated memory for strings and nodes and when I run valgrind(a debugger which detects memory leaks) it says no memory leaks are possible, but it says that there is an error Uninitilised value was created by a heap allocation and redirects me to that exact line where I'm allocating memory for dictionary the exact first line of the load function which I've written above.What am I doing wrong? I guess the way I use dictionary globally is wrong, so can anybody suggest some other way of keeping it global and avoid this error?
In the updated code you use an uninitialized pointer:
dictionary = malloc(sizeof(node*) * LISTS);
// .... code that does not change dictionary[i] for any i
new_node->next = dictionary[index]; // use uninitialized pointer
As people had wrote already, this will only work if you had pre-set all the pointers to be NULL before entering this loop:
dictionary = malloc(sizeof(node*) * LISTS);
if ( !dictionary ) {
return false;
}
for (size_t i = 0; i < LISTS; ++i) {
dictionary[i] = NULL;
}
The heap allocation you assign to dictionary uses malloc which does not initialize the returned bytes. So dictionary in the code you've posted ends up being an array of uninitialized pointers. Presumably you go on to use those pointers in some way which valgrind knows to be an error.
An easy way to fix this is to use calloc instead of malloc, because it zeros the returned bytes for you. Or, use memset to zero the bytes yourself.