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.
Related
I'm still learning how to program in C and I've stumbled across a problem.
Using a char array, I need to create a linked list, but I don't know how to do it. I've searched online, but it seems very confusing. The char array is something like this char arr[3][2]={"1A","2B","3C"};
Have a look at this code below. It uses a Node struct and you can see how we iterate through the list, creating nodes, allocating memory, and adding them to the linked list. It is based of this GeeksForGeeks article, with a few modifications. I reccommend you compare the two to help understand what is going on.
#include <stdio.h>
#include <stdlib.h>
struct Node {
char value[2];
struct Node * next;
};
int main() {
char arr[3][2] = {"1A","2B","3C"};
struct Node * linked_list = NULL;
// Iterate over array
// We calculate the size of the array by using sizeof the whole array and dividing it by the sizeof the first element of the array
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
// We create a new node
struct Node * new_node = (struct Node *)malloc(sizeof(struct Node));
// Assign the value, you can't assign arrays so we do each char individually or use strcpy
new_node->value[0] = arr[i][0];
new_node->value[1] = arr[i][1];
// Set next node to NULL
new_node->next = NULL;
if (linked_list == NULL) {
// If the linked_list is empty, this is the first node, add it to the front
linked_list = new_node;
continue;
}
// Find the last node (where next is NULL) and set the next value to the newly created node
struct Node * last = linked_list;
while (last->next != NULL) {
last = last->next;
}
last->next = new_node;
}
// Iterate through our linked list printing each value
struct Node * pointer = linked_list;
while (pointer != NULL) {
printf("%s\n", pointer->value);
pointer = pointer->next;
}
return 0;
}
There are a few things the above code is missing, like checking if each malloc is successful, and freeing the allocated memory afterwards. This is only meant to give you something to build off of!
It looks there is no duplicate questions...so i want a function to free all nodes in a single linked list, and i want to do it recursively. I come up with a close one that i thought it would work, but it does not. It seems that after it removed one node, the upper stack function will not excuse, recursively. I am wondering how to modify the code to make it work.
#include <stdlib.h>
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node *next;
};
void ft_list_clear(struct Node *begin_list)
{
if ((begin_list->next))
ft_list_clear(begin_list->next);
if(!(begin_list->next))
{
free(begin_list);
begin_list = NULL;
}
}
int main()
{
struct Node* head = NULL;
struct Node* second = NULL;
struct Node* third = NULL;
// allocate 3 nodes in the heap
head = (struct Node*)malloc(sizeof(struct Node));
second = (struct Node*)malloc(sizeof(struct Node));
third = (struct Node*)malloc(sizeof(struct Node));
head->data = 1; //assign data in first node
head->next = second; // Link first node with second
second->data = 2; //assign data to second node
second->next = third;
third->data = 3; //assign data to third node
third->next = NULL;
ft_list_clear(head);
return 0;
}
You're pretty close
void ft_list_clear(struct Nude *list)
{
if (!list) { return; }
ft_list_clear(list->next);
list->next = null;
free(list);
}
Explaining the code
The first if checks if the list is currently null and exits the recursion if so.
If the list isn't null recursively call the function.
This repeats until the end of the list null.
Then since the next has been cleared by the recursive call you can set it to null in this call (not strictly necessary since this clears everything).
Finally actually free this node prior to returning to the previous call (this node's parent).
You can also do the delete in the opposite order if you want
void ft_list_clear(string Node *list)
{
if (!list) { return; }
struct Node *next = list->next;
free(list);
ft_list_clear(next);
}
Same principles just deletes this node before going to the next. This means you don't need to fix the next pointers but you will need to copy them first so you don't lose the reference.
I think it's because you're just freeing nodes, but you miss to nullify the next members. Try this. I haven't run this so goodluck.
void ft_list_clear(struct Node *begin_list)
{
if ((begin_list->next))
{
ft_list_clear(begin_list->next);
begin_list->next = NULL; // <-- you should nullify the next of the current after you free the node which it points to.
}
if(!(begin_list->next)) // <-- even if the next node was deleted, this won't run if you didn't nullify begin_list->next
{
free(begin_list);
begin_list = NULL;
}
}
`void ft_list_clear(struct Node *begin_list)
{
if ((begin_list->next))
ft_list_clear(begin_list->next);
free(begin_list);
begin_list = NULL;
printf("God bless America");
}`
Hopefully, if God blesses America thrice, your code is working, I've committed some changes in your code, and all I did was remove the second if statement because usually, we don't need that in recursion (I'm not saying we don't need more than one if statement). Test it yourself and you'll understand why it's so. Hope it helps.
The problem with this
void ft_list_clear(struct Node *begin_list)
{
if ((begin_list->next))
ft_list_clear(begin_list->next);
if(!(begin_list->next))
{
free(begin_list);
begin_list = NULL;
}
}
is:
In the first call, begin_list is equal to head.
head->next is not NULL, so ft_list_clear(second) is executed
second->next is not NULL, so ft_list_clear(third) is executed
third->next is NULL, so free(third) happens. The begin_list = NULL
line does nothing here, its pointless.
The third iteration returns, back to the second. The next line to execute is
if(!(begin_list->next))
begin_list->next is not NULL (it's been only freed), hence the condition is evaluated to false and the free
is not executed.
Same happens with the first iteration.
This is recursion that would work:
void ft_list_clear(struct Node *begin_list)
{
if(begin_list == NULL)
return;
ft_list_clear(begin_list->next);
free(begin_list);
}
Recursively deleting a linked is a bad idea. Each recursive call requires a stack frame, and whether you free the list node memory as you descend or ascend the recursion, you need O(n) memory for what is a simple operation. Each stack call requires local variable storage, room for return pointer, previous stack frame pointer, and possibly other stuff (at least 12-24 bytes per node).
Better to iterate through the list. I have provided three variants of the free (iterative, and two recursive, one free's node on descent, one free's node on ascent).
#include <stdlib.h>
#include <stdio.h>
typedef struct Node
{
int data;
struct Node* next;
} node_t;
long list_clear_iter(node_t* p) {
if(!p) return 0;
long n = 0; //count;
for( ; p->next; ) {
node_t* fp = p; //free this
p = p->next;
free(fp);
++n;
}
return n;
}
//list clear, recursive, (pre)free
long ft_list_clear_pr(node_t* p) {
if(!p) return 0;
node_t* np = p->next;
free(p); //free on descend
long n = ft_list_clear_pr(np);
return(n+1);
}
//list clear recursive, free(post)
long ft_list_clear_rp(node_t* p) {
if(!p) return 0;
long n = ft_list_clear_rp(p->next);
free(p); //free on ascend
return(n+1);
}
I have written a piece of code that takes several integers (as many as 100 000 int) as input from a file and stores them in a "recursive" struct.
As long as I run this code on my PC everything is fine.
Here is the code:
typedef struct node{
int data;
struct node* next;
} node;
...
node* create(void){
node* list = (node*)malloc(sizeof(node));
return list;
}
node* insert(node* list, int temp){
if(list == NULL){
list = create();
list->data = temp;
list->next = NULL;
return list;
}
list->next = insert(list->next, temp);
return list;
}
int main(void){
...
node* list = NULL;
while(there is still data to input){
list = insert(list, data);
}
}
However, when I try to run this code on my Android phone, I get a
malloc stack overflow error
(I know that the stack space reserved on a phone is less then the one on a PC).
The problem is that, to my knowledge, this program should use a lot of stack memory.
This is what I think is happening inside my program (please correct me if I am wrong):
1). node* list = NULL ==> Space for a pointer (8 byte) is allocated on the stack;
2). list = insert(list, temp) ==> Goes to the end of data stream.
3). list = create() ==> The create() function is called;
4). node* list = (node*)malloc(sizeof(node)) ==> Space for a pointer is allocated on the stack (8 byte) and space for the struct is allocated on the heap (16 byte);
5). return list ==> create() function is closed, therefore the variable node* list on the stack is "freed" while the space allocated on the heap remains.
So my program should be using a lot of heap memory, but just 8 byte of stack memory (the ones needed for the first pointer in main ==> node* list = NULL), how is it possible that I get error:
malloc stack overflow
?
Thank you
Lorenzo
P.s. Sorry guys but I was trying to make my code shorter, but what I had written was no sense. I fixed it now (or I hope so).
You are overusing the variable list.
You need to retain a pointer your current node instead of overwriting it with the line:
list = create();
consider the following or similar:
int main(void){
...
node* list = NULL;
node* current = NULL;
node* next = NULL;
while(...){
...
next = create();
if(list == NULL) //list empty case
{
list = next;
current = next;
}
current->next = next;
next->next = NULL;
current = next;
}
}
I encourage you to wrap some of this logic in a function separate from main().
The actual cause of the segmentation fault is not in the code you showed, but in your current code when every you try to use list it is NULL, which is probably your undefined behavior.
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.
Why are the split lists always empty in this program? (It is derived from the code on the Wikipedia page on Linked Lists.)
/*
Example program from wikipedia linked list article
Modified to find nth node and to split the list
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct ns
{
int data;
struct ns *next; /* pointer to next element in list */
} node;
node *list_add(node **p, int i)
{
node *n = (node *)malloc(sizeof(node));
if (n == NULL)
return NULL;
n->next = *p; //* the previous element (*p) now becomes the "next" element */
*p = n; //* add new empty element to the front (head) of the list */
n->data = i;
return *p;
}
void list_print(node *n)
{
int i=0;
if (n == NULL)
{
printf("list is empty\n");
}
while (n != NULL)
{
printf("Value at node #%d = %d\n", i, n->data);
n = n->next;
i++;
}
}
node *list_nth(node *head, int index) {
node *current = head;
node *temp=NULL;
int count = 0; // the index of the node we're currently looking at
while (current != NULL) {
if (count == index)
temp = current;
count++;
current = current->next;
}
return temp;
}
/*
This function is to split a linked list:
Return a list with nodes starting from index 'int ind' and
step the index by 'int step' until the end of list.
*/
node *list_split(node *head, int ind, int step) {
node *current = head;
node *temp=NULL;
int count = ind; // the index of the node we're currently looking at
temp = list_nth(current, ind);
while (current != NULL) {
count = count+step;
temp->next = list_nth(head, count);
current = current->next;
}
return temp; /* return the final stepped list */
}
int main(void)
{
node *n = NULL, *list1=NULL, *list2=NULL, *list3=NULL, *list4=NULL;
int i;
/* List with 30 nodes */
for(i=0;i<=30;i++){
list_add(&n, i);
}
list_print(n);
/* Get 1th, 5th, 9th, 13th, 18th ... nodes of n etc */
list1 = list_split(n, 1, 4);
list_print(list1);
list2 = list_split(n, 2, 4); /* 2, 6, 10, 14 etc */
list_print(list2);
list3 = list_split(n, 3, 4); /* 3, 7, 11, 15 etc */
list_print(list3);
list3 = list_split(n, 4, 4); /* 4, 8, 12, 16 etc */
list_print(list4);
getch();
return 0;
}
temp = list_nth(current, ind);
while (current != NULL) {
count = count+step;
temp->next = list_nth(head, count);
current = current->next;
}
You are finding the correct item to begin the split at, but look at what happens to temp from then on ... you only ever assign to temp->next.
You need to keep track of both the head of your split list and the tail where you are inserting new items.
The program, actually, has more than one problem.
Indexes are not a native way to address linked list content. Normally, pointers to nodes or iterators (which are disguised pointers to nodes) are used. With indexes, accessing a node has linear complexity (O(n)) instead of constant O(1).
Note that list_nth returns a pointer to a "live" node within a list, not a copy. By assigning to temp->next in list_split, you are rewiring the original list instead of creating a new one (but maybe it's intentional?)
Within list_split, temp is never advanced, so the loop just keeps attaching nodes to the head instead of to the tail.
Due to use of list_nth for finding nodes by iterating through the whole list from the beginning, list_split has quadratic time (O(n**2)) instead of linear time. It's better to rewrite the function to iterate through the list once and copy (or re-attach) required nodes as it passes them, instead of calling list_nth. Or, you can write current = list_nth(current, step).
[EDIT] Forgot to mention. Since you are rewiring the original list, writing list_nth(head, count) is incorrect: it will be travelling the "short-cirquited" list, not the unmodified one.
I also notice that it looks like you are skipping the first record in the list when you are calculating list_nth. Remember is C we normally start counting at zero.
Draw out a Linked List diagram and follow your logic:
[0]->[1]->[2]->[3]->[4]->[5]->[6]->[7]->[8]->[9]->...->[10]->[NULL]
Your description of what list_split is supposed to return is pretty clear, but it's not clear what is supposed to happen, if anything, to the original list. Assuming it's not supposed to change:
node *list_split(node *head, int ind, int step) {
node *current = head;
node *newlist=NULL;
node **end = &newlist;
node *temp = list_nth(current, ind);
while (temp != NULL) {
*end = (node *)malloc(sizeof(node));
if (*end == NULL) return NULL;
(*end)->data = temp->data;
end = &((*end)->next);
temp = list_nth(temp, step);
}
return newlist; /* return the final stepped list */
}
(You probably want to factor a list_insert routine out of that that inserts a new
node at a given location. list_add isn't very useful since it always adds to the
beginning of the list.)