Here is the code for freeing the whole linked list
void free_list(RecordType *list)
{
RecordType *tempNode; /* temporary Node to hold on the value of previous node */
while(list != NULL) /* as long as the listnode doesn't point to null */
{
tempNode = list; /* let tempNode be listNode in order to free the node */
list = list->next; /* let list be the next list (iteration) */
free(tempNode); /* free the node! */
}
}
I think this code itself is working ok (?), but I have no idea how to check.
I only applied the theory (e.g. # of frees must = to the # of mallocs)
So here are some questions that I'm wondering...
Does this method work?
Do I need to malloc tempNode?
I initialized tempNode before while loop... but after I free, tempNode still works... I don't really get that part
The theory that I used:
# of free() == # of malloc()
You need a temporary node to hold the current node
Let the current node equal to the next node
Free the current node by using the temporary node
If any of my theory sounds wrong, please explain!
Thanks!
Does this method work?
Yes, assuming the list nodes were all dynamically allocated and haven't been previously freed
Do I need to malloc tempNode?
You don't need to allocate any memory inside free_list but all list elements must have been dynamically allocated previously. You can only call free on memory that was allocated using malloc (or calloc)
I initialized tempNode before while loop... but after I free, tempNode
still works... I don't really get that part
Calling free returns ownership of memory to the system. It may choose to reuse this memory immediately or may leave it untouched for some time. There's nothing to stop you accessing the memory again but the results of reading or writing it are undefined.
If you want to make it harder for client code to accidentally access freed memory, you could change free_list to NULL their pointer
void free_list(RecordType **list)
{
RecordType *tempNode;
while(*list != NULL) {
tempNode = *list;
list = tempNode->next;
free(tempNode);
}
*list = NULL;
}
If you also want to check that you really have freed all memory, look into using valgrind. This will report any memory leaks and also flags some types of invalid memory access.
The method certainly works - but it should be mallocd first before freeing. Otherwise it is undefined behavior.
You don't need to malloc() tempNode only if list has been previously malloc()d.
The third part is undefined behavior. After free() the data may still exist, but is flagged for being overwritten. You cannot rely on the node once it is free()d
The best way to check your code is interactive tracing by means of Debugger. Gdb in KDevelop on Linux or MS Visual Studio's debugger on MS Windows are perfect. I'll use the later for this demonstration.
This code defines a uni-directed list of integers with three functions: ListPush() adds an integer to the list, ListPrint() displays the list contents and ListDestroy() destroys the list. In main() I insert 3 integers into the list, print them and destroy the list.
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct Node NODE, *PNODE;
typedef struct Node {
int item;
PNODE next;
};
PNODE ListPush(PNODE head, int item) {
PNODE p;
PNODE n = (PNODE) malloc(sizeof(NODE));
if ( !n ) exit(1);
n->next = 0;
n->item = item;
if (!head) {
head = n;
}
else {
for ( p=head; p->next != 0; p=p->next );
p->next = n;
}
return head;
}
void ListPrint(PNODE head) {
PNODE p;
printf("List contents:\n\n");
for (p=head; p!=0; p=p->next) {
printf("%d ", p->item );
}
}
void ListDestroy( PNODE head ) {
PNODE n, c = head;
if ( !head ) return;
do {
n = c->next;
free(c);
c = n;
} while (c );
}
int main() {
int i;
int a[3] = {1,2,3};
PNODE head = 0;
for ( i = 0; i<3; ++i ) {
head = ListPush(head, a[i]);
}
ListPrint(head);
ListDestroy(head);
return 0;
}
Three attached images illustrate 2 stages of the program (MSVS2012 Debugger).
The first shows state of relevant local vars after for() cycle finishes. Look at head variable and proceed on the tree. You can see three nodes with their contents: integers 1,2 and 3 respectively.
The second image shows the variables inside ListDestroy() after first call to free(). You can see that head points to freed memory (red circles) and pointer in variable c points to the next node being destroyed on the next loop.
Related
I am trying to add an item to the linked list by traversing the list to create the next node. and the last node in the list to point to the newly created node. But I am getting a core dump segmentation fault on it.
void linked_list_add(list_node_t *head, void *item)
{
list_node_t *temp = head;
while(temp->next != NULL)
{
temp = temp->next;
}
list_node_t *new_node = (list_node_t *)malloc(sizeof(list_node_t));
new_node->data = item;
new_node->next = NULL;
new_node->prev = temp;
//if(temp != NULL)
// temp->next = new_node;
// new_node->prev = temp;
}
TEST_F(LinkedList, Add)
{
int i = 3;
linked_list_add(list, &i);
ASSERT_EQ(list->next->data, &i);
i = 4;
linked_list_add(list, &i);
ASSERT_EQ(list->prev->data, &i);
i = 5;
linked_list_add(list, &i);
ASSERT_EQ(list->next->data, &i);
}
This is an answer to summarize the comments.
There are likely at least 3 issues with the code as written:
When the code void linked_list_add(list_node_t *head, void *item) is passed arguments, you generally want to be able to handle a NULL pointer for head. It looks like the while loop immediately goes into searching for the end of the list even if the head is null.
The newly added node, new_node gets the prev pointer updated so that the backwards searchs will be and shouldn't segfault. However, the forward searching isn't preserved. By this I mean that the last non-NULL node in the linked list doesn't have the next pointer pointing to the new_node.
The test ASSERT_EQ(list->prev->data, &i); is likely accessing either a random memory location or a NULL pointer. Given that the OP didn't post the declaration of the list struct it is difficult to say what the default values are/will be. However, unless this list is circular, the value of list->prev is an uninitialized pointer. Depending on your setup (e.g. if there is setup code for the linked list that sets the pointers to null, you could be accessing a NULL pointer there too.
I hope this helps the OP solve their coding problem(s).
I'm pretty new to C programming.
I have an assignment in which we are supposed to create a doubly linked list of integers, and write some functions to manipulate them. We are being asked to prevent memory leaks, but I'm not really sure how to do that.
I have to malloc a bunch of times in order to create and store nodes when making the linked list, and I'm pretty sure it's not a good idea to malloc enough space for a node and then free the pointer to it in the same place.
Therefore, my best guess is that I should free all nodes in the main function, when I will have printed their contents to the screen and they are no longer needed. I tried to implement a kill function that takes as input a reference head to the first node in the list, and which iterates over the nodes, freeing them as they go.
I went as far as installing valgrind to try and see if there were any memory leaks, and it looks like there are still some. I have no idea where they are coming from or how to fix the issue.
Here is the whole code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct Node{
int data;
struct Node *next;
struct Node *previous;
}Node;
void print_dll(Node *head){
Node *curr = head;
while(curr != NULL){
printf("%d\t", curr->data);
curr = curr->next;
}
puts(" ");
}
Node* create_dll_from_array(int array [], int arrSize){
//this is a function that creates a doubly linked list
//with the contents of the array
Node* current = (Node *) malloc (sizeof(Node * ));
current->data = array[arrSize-1];
current -> next = NULL;
for(int i = 2; i <= arrSize; i++){
//create a new node
Node * temp = (Node*)malloc(sizeof(Node*));
//I would like the dll to be in the same order as the array, I guess it isn't strictly necessary
temp ->data = array[arrSize-i];
temp -> next = current;
current-> previous = temp;
//now make temp the current
current = temp;
}
current-> previous = NULL;
return current;
}
void insert_after(Node* head, int valueToInsertAfter, int valueToInsert ){
if(head != NULL){
Node * current = head;
while(current-> data != valueToInsertAfter){
//this while loop brings 'current' to the end of the list if
//the searched value is not there
if(current-> next != NULL){
current = current->next;
}else{
break;
}
}
//after exiting this loop, the current pointer is pointing
//either to the last element of the dll or to the element
//we need to insert after
Node *new = (Node *) malloc (sizeof(Node *));
new->data = valueToInsert;
new->next = current->next;
new->previous = current;
if(current->next != NULL){
(current->next)->previous = new;
}
current->next = new;
}
}
void delete_element(Node* head, int valueToBeDeleted){
//work in progress
}
void kill(Node *head){
//this is my attempt at freeing all the nodes in the doubly linked list
Node *current;
while(head!=NULL){
current = head;
head = head->next;
free(head);
}
}
int main(){
int array [5] = {11, 2, 7, 22, 4};
Node *head;
/*Question 1*/
//creates a doubly linked list from the array below
head = create_dll_from_array(array, 5); ///size of the array is 5
/* Question 2 */
// print_dll(head);
/*Question 3*/
// to insert 13 after the first appearance of 7
insert_after(head, 7, 13);
print_dll(head);
//to insert 29 after first appearance of 21
insert_after(head, 21, 29);
print_dll(head);
/*Question 6*/
//create a function to free the whole list
kill(head);
return 0;
}
The main function here is given to us by the prof, we have to build out function around it.
I don't know why this is still appearing to lead to memory leaks, and if I', being honest, I don't really know where else they could occur. As far as I know, I need to keep all the memory until almost the last minute.
Please help, I'm pretty lost here.
Thank you!
There are two problems:
Need to change all malloc (sizeof(Node*)) to malloc (sizeof(Node))
Need to change free(header) to free(current) in the kill function.
The modified code is as follows
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
struct Node *previous;
} Node;
void print_dll(Node *head)
{
Node *curr = head;
while(curr != NULL) {
printf("%d\t", curr->data);
curr = curr->next;
}
puts(" ");
}
Node *create_dll_from_array(int array [], int arrSize)
{
//this is a function that creates a doubly linked list
//with the contents of the array
Node *current = (Node *) malloc (sizeof(Node));
current->data = array[arrSize - 1];
current -> next = NULL;
for(int i = 2; i <= arrSize; i++) {
//create a new node
Node *temp = (Node *)malloc(sizeof(Node));
//I would like the dll to be in the same order as the array, I guess it isn't strictly necessary
temp ->data = array[arrSize - i];
temp -> next = current;
current-> previous = temp;
//now make temp the current
current = temp;
}
current-> previous = NULL;
return current;
}
void insert_after(Node *head, int valueToInsertAfter, int valueToInsert )
{
if(head != NULL) {
Node *current = head;
while(current-> data != valueToInsertAfter) {
//this while loop brings 'current' to the end of the list if
//the searched value is not there
if(current-> next != NULL) {
current = current->next;
} else {
break;
}
}
//after exiting this loop, the current pointer is pointing
//either to the last element of the dll or to the element
//we need to insert after
Node *new = (Node *) malloc (sizeof(Node));
new->data = valueToInsert;
new->next = current->next;
new->previous = current;
if(current->next != NULL) {
(current->next)->previous = new;
}
current->next = new;
}
}
void delete_element(Node *head, int valueToBeDeleted)
{
//work in progress
}
void kill(Node *head)
{
//this is my attempt at freeing all the nodes in the doubly linked list
Node *current;
while(head != NULL) {
current = head;
head = head->next;
free(current);
}
}
int main()
{
int array [5] = {11, 2, 7, 22, 4};
Node *head;
/*Question 1*/
//creates a doubly linked list from the array below
head = create_dll_from_array(array, 5); ///size of the array is 5
/* Question 2 */
// print_dll(head);
/*Question 3*/
// to insert 13 after the first appearance of 7
insert_after(head, 7, 13);
print_dll(head);
//to insert 29 after first appearance of 21
insert_after(head, 21, 29);
print_dll(head);
/*Question 6*/
//create a function to free the whole list
kill(head);
return 0;
}
Change sizeof(Node * ) to sizeof(Node) due to malloc reserving you memory for which the pointer points to and it needs the correct amount of needed memory (which is not a pointer but the object itself).
i <= arrSize might be an overflow, since the size usually is given as amount of memory cells. So you might consider using i < arrSize
The first while loop in the insert_after might point to invalid memory after the array
Node *new = is ugly syntax, since new is a keyword in C++. Please never do that, since that will break any code, which is being used in C++.
You dont need a temporary element in kill(). You can instead going until head points to NULL.
delete_element needs the same array checks as insert_after
Probably you need to debug the whole thing pasting one function after the other to get it properly working. No guarantee for correctness, since that was abit hard to read without comments and all.
The best way to find memory leaks is using valgrind (or a similar tool) in run time.
Valgrind will identify any memory leak or violation you ran through.
to run valgrind in linux environment, all you need to do is:
# valgrind --leak-check=full ./my_program
In you case it gave mainy theses errors:
==28583== Invalid read of size 8
==28583== at 0x400871: kill (aaa.c:77)
==28583== by 0x40092D: main (aaa.c:103)
==28583== Address 0x5204188 is 0 bytes after a block of size 8 alloc'd
==28583== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28583== by 0x40073A: create_dll_from_array (aaa.c:29)
==28583== by 0x4008D9: main (aaa.c:87)
this error means the allocation size was too small. as mentioned in another answers it is because you allocate enough memory for a pointer and not for the struct.
So I'm trying to do a method to clear a doubly linked list for school where the doubly linked list and nodes are defined as:
struct word_entry
{
char *unique_word ;
int word_count ;
} ;
struct node
{
struct word_entry one_word ;
struct node *p_previous ;
struct node *p_next ;
} ;
struct linked_list
{
struct node *p_head ;
struct node *p_tail ;
struct node *p_current ;
} ;
I have a method to clear a linked list by doing
int clear_linked_list( struct linked_list *p_list ) //return how many nodes were cleared
{
if (p_list->p_head == NULL) {
return 0;
}
else {
int count = 0;
struct node *curr = p_list->p_head;
while (curr != NULL) {
struct node *next = curr->p_next;
free(curr->one_word.unique_word);
free(curr);
curr = next;
count++;
}
return count;
}
}
I do a free() on curr->one_word.unique_word because it's a malloc'd char array. I was taught to free when I use malloc, so that's there.
The issue I run into is I get a "bogus pointer (double free?)" and a core dump when I run the test file provided by my professor. I've worked on this for a few hours and can't seem to find out where (or how) I'm calling free twice.
When you loop through the list, you should constantly change the position of the head, so that even if you repeat clear_linked_list, you will not get an error.
int clear_linked_list(struct linked_list* p_list) // return how many nodes were cleared
{
if (p_list->p_head == NULL) {
return 0;
} else {
int count = 0;
while (p_list->p_head != NULL) {
struct node* curr = p_list->p_head;
p_list->p_head = p_list->p_head->p_next;
free(curr->one_word.unique_word);
free(curr);
count++;
}
return count;
}
}
When freeing memory it is a good practice to set NULL to pointers that were freed to avoid this kind of problems.
So you should do:
free(curr->one_word.unique_word);
curr->one_word.unique_word=NULL;
//if that one_word.unique_word was shared between multiples nodes that free could cause problems if you dont set it to NULL afterwards
free(curr);
curr=NULL; //or curr=next...
Also. Check that when you create the nodes that:
*p_next is NULL on the last node of the double linked list
*p_previous is NULL on the first node of the list
You don't null out p_head before you leave the clear function.
So, if you called it twice, you'd have problems (i.e. p_head would point to an already freed node). Likewise for p_tail.
Also, if you tried to add to the list again, you'd have similar problems.
Otherwise, your clear code is just fine.
So, can you prove that the list is constructed correctly (e.g. before you free, add a printf that prints out all the node's pointers before you free anything).
I am trying to work with a doubly linked list of SAT grades between 200-800. I need to remove from all the duplicates from the list, i.e. make sure each grade appears only once by deleting all its duplicates.
#define HIGHEST_GRADE 800
typedef struct dListNode{
int* dataPtr;
struct dListNode* next;
struct dListNode* prev;
}DListNode;
typedef struct dList
{
DListNode* head;
DListNode* tail;
}DList;
void removeDuplicates(DList* lst)
{
int i;
int gradesBucket [numOfGrades];
DListNode* temp;
temp = lst->head;
for(i=200 ; i<HIGHEST_GRADE ; i++) /*creating 600 buckets - each bucket for a grade*/
gradesBucket[i] = FALSE;
while (temp)
{
if ((gradesBucket [*temp->dataPtr]) == TRUE) /*if current grade has already */
/* appeared earlier on the list */
{
deleteFromList (temp); /*delete that grade's cell*/
}
else
gradesBucket[*temp->dataPtr] = TRUE; /* mark grade bucket as true, meaning */
/* the grade already appeared*/
temp = temp->next; /*moving on to next grade*/
}
}
void deleteFromList(DListNode* toRemove)
{
toRemove->prev->next = toRemove->next;
toRemove->next->prev = toRemove->prev;
deAllocateListCell (toRemove);
}
void deAllocateListCell (DListNode* cell)
{
free (cell->dataPtr);
free (cell);
}
Please help me understand what's wrong.
here's the fixed code, which still doesn't work properly. Now it compiles but nothing is shown on screen. And by the way, I dont need to take care of deleting the head, because the first number can never be a duplicate... but I took care of it in case the head was NULL;
I also send the previous cell of the one i want to delete, to the function deleteFromList. It still doesn't work. Any ideas? Thanks!
void deleteFromList(DList* lst, DListNode* p)
{
DListNode* del_cell = p->next; /* cell to delete*/
if (p->next->next == NULL) /*if cell to remove is the tail*/
{
deAllocateListCell (p->next); /* freeing current tail */
lst->tail = p; /* p is the new tail */
p->next = NULL; /* tail points to NULL */
}
else /* if cell to remove is not the tail (note: can't be head beacuse no duplicates can be found in the first grade) */
{
p->next = del_cell->next;
del_cell->next->prev = p;
deAllocateListCell (del_cell);
}
}
The code of your function deleteFromList() doesn't account for (literal) edge cases: Deleting the first or last node of a list.
Plus, your code dereferences a pointer to a deallocated node; the pointer can become outright invalid, or the free() function can overwrite its contents (as the Microsoft Debug C RunTime is known to).
Try to be specific - What is it that doesn't work? Does your code compile? Do you get an error during runtime? You don't get the results you expected in a scenario?
Your deleteFromList function should take care of removing the head or tail (That is when toRemove->prev or toRemove->next are null (respectively).
temp = lst->head; and what happens when lst is null? You'll get a run-time error
You are not updating head or tail in case they are deleted
That's what I found in first glance.
you should write while(temp->next) for correcting this....and also u can simply deallocate the node using free. and to eliminate dangling pointer problem you should make it NULL after freeing that node
I've written a simple program including linked lists. When I try to display the linked list in the function it is created, it works alright; however, when I return to main and try to display it, it does not work properly.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#define LEN 20
struct Node {
char word[LEN];
int count;
Node * next;
};
Node* build_linked_list (char array[][LEN], int N);
Node* make_ordered_copy (Node * head);
void print_list(Node * head);
int main()
{
#define NUMBER 10
char array[NUMBER][LEN]; // array which the words will be recorded
int N=6;
for(int a=0; a<N; ++a) strcpy(array[a], "test");
print_list(build_linked_list(array, N));
getchar();
return 0;
}
Node* build_linked_list (char array[][LEN], int N)
{
Node ndArray[N];
Node *head, *newnode;
head = &ndArray[0];
strcpy(head->word, array[0]); // writing the first element to the head
head->count = 0;
head->next = NULL;
for(int a=1; a<N; ++a) // writing the elements in a linked list
{
newnode = &ndArray[a];
strcpy(newnode->word, array[a]);
newnode->count = 0;
newnode->next = head; // first location now becomes second location
head = newnode;
}
print_list(head);
printf("Previous values were shown in build_linked_list\n");
return head;
}
void print_list(Node* head)
{
Node* traverse;
traverse = head;
while(traverse) // while traverse is not NULL
{
printf("\"%s\" with the frequency of %d\n", traverse->word, traverse->count);
traverse = traverse->next;
}
return;
}
When debugging in print_list function called from main, "traverse->word" shows the correct value at first but it does not print it right and then it changes to some another value.
The elements of ndArray are only valid within the scope of build_linked_list. Once the program exists that function, any access to those elements yields undefined behavior. You are building your linked list with those elements. Instead, you need to allocate memory for the nodes of your linked list on the heap, or you need to move the ndArray variable to the file scope so that it exists for the lifetime of your main method.
Inside build_linked_list you are declaring
Node ndArray[N];
which is the Node you are filling within that function. But when the execution ends the block of the build_linked_list function ndArray is deallocated therefore the pointer returned points to the memory previously allocated for ndArray but that now is no more occupied by that array.
The usual implementation of linked lists is done using the heap and dynamic memory allocation.
When building your Node array you should write:
Node* ndArray = new ndArray[N];
But at this point I higly suggest you to read about Garbage collectors and how to properly manage dynamic memory, because this is a whole new topic which has been discussed over and over here on StackOverflow.
For this answer I'll just anticipate to you that when allocating with new or new[] (for malloc and free see the C reference) you should as well delete with delete or delete[] respectively. In this case, when you are done with a linked list or ndArray you should write:
delete[] ndArray;
to free the memory. Otherwise you'll have a memory leak.