Freeing Memory after Linked List Removal - c

I am learning C in my free time. I am familiar with C#, Java, and Python. As an exercise, I wrote a linked list in C. It functions correctly, has error handling, etc.
However, I am trying to fix memory leaks. I know that C does not have automatic garbage collection. So how do I "free" a member of a list after I have removed it? I wrote a function called removeAllList(). The function removes the member from the list successfully, but I know that the memory for that member is still allocated. I have tried using the free([myArgument]) function, but it results in infinite loops. Can you show where I would use the free() function to successfully deallocate memory for the removed member in my code?
#include<stdio.h>
#include<stdlib.h>
struct Member{
int data;
struct Member *next;
};
struct List{
int size;
struct Member *root;
};
struct Member *createMember(int i){
struct Member *new;
new = malloc(sizeof(struct Member));
new->data = i;
new->next = NULL;
return new;
}
struct List *createList(int i){
struct List *new;
new = malloc(sizeof(struct List));
new->root = createMember(i);
new->size = 1;
return new;
}
void printList(struct List *list){
struct Member *current = list->root;
//error handling for empty list
if(list->size < 1){
printf("Error: List is empty");
}
//if list is not empty
else{
printf("List size: %i\nContents: ", list->size);
while(current->next != NULL){
printf("%i, ", current->data);
current = current->next;
}
printf("%i\n", current->data);
}
}
void addList(struct List *list, int i){
struct Member *current = list->root;
while(current->next != NULL){
current = current->next;
}
current->next = createMember(i);
list->size++;
}
void removeAllList(struct List *list, int i){
struct Member *current = list->root;
struct Member *prev = list->root;
if(list->size < 1){
//list is empty, end function now
return;
}
//remove all matching list head
while(current->data == i){
if(list->size <= 1){
list->root = NULL;
list->size--;
//list is empty, end function now
return;
}
else{
list->root = current->next;
current = list->root;
list->size--;
}
}
current = current->next;
//remove all matching list body
while(current->next != NULL && list->size > 1){
if(current->data == i){
prev->next = current->next;
list->size--;
}
prev = current;
current = current->next;
}
//remove all matching list tail
if(current->data == i && list->size > 1){
prev->next = NULL;
list->size--;
}
}
main(){
struct List *myList;
myList = createList(4);
addList(myList, 12);
addList(myList, 9);
addList(myList, 4);
addList(myList, 43);
addList(myList, 4);
printList(myList);
removeAllList(myList, 4);
printList(myList);
}

You shouldn't make things that consume memory NULL. You need to free() them like this: free(mynode->next); but only when you're sure there was a malloc() call before.
In your code, you need to use free(list->root); instead of list->root = NULL; and free(prev->next); instead of prev->next = NULL;

Related

unable to insert into ordered linked list in C

i am new to programming and C. I am trying to create an ordered linked list. For some reason which i cannot figure out, it never enters the first if block in the insert_in_order function even though my linked list is empty when i call the insert_in_order function. Would anyone have any idea what i am doing wrong?
Here is my code:
#include <stdio.h>
#include <stdlib.h>
struct node {
int value;
struct node* next;
};
typedef struct node node_t;
void printlist(node_t *head){
node_t *temporary = head;
while(temporary != NULL){
printf("%d - ", temporary->value);
temporary = temporary->next;
}
printf("\n");
}
node_t *create_new_node(int value){
node_t *result = malloc(sizeof(node_t));
result->value = value;
result->next = NULL;
//printf("result.value = %d\n", result->value);
return result;
}
void insert_in_order (node_t *head, node_t *node_to_insert){
node_t *current_node = head;
node_t *prior_node = head;
//linked list is empty
if (head == NULL){ //never enters if block for some reason
head->next = node_to_insert;
node_to_insert->next = NULL;
break;
//printf("inside the if stmt");
}
if(node_to_insert->value <= current_node->value){
head->next = node_to_insert;
node_to_insert->next = current_node;
break;
}
current_node = current_node->next;
while (current_node->next != NULL){
if(node_to_insert->value <= current_node->value){
node_to_insert->next = current_node;
prior_node->next = node_to_insert;
break;
}
else if (node_to_insert > current_node){
current_node = current_node->next;
prior_node = prior_node->next;
}
}
//node to insert is the largest in the linked list
current_node->next = node_to_insert;
node_to_insert->next = NULL;
}
int main(){
node_t *head;
node_t *node1;
node_t *node2;
head = NULL;
node1 = create_new_node(22);
node2 = create_new_node(33);
printf("node1's value equals %d\n", node1->value);
printf("node2's value equals %d\n", node2->value);
insert_in_order(head, node1);
printlist(head);
}
First of all this code does not compile - these breaks are invalid
if (head == NULL){ //never enters if block for some reason
head->next = node_to_insert;
node_to_insert->next = NULL;
break; <<<<====
//printf("inside the if stmt");
}
and
if (node_to_insert->value <= current_node->value) {
head->next = node_to_insert;
node_to_insert->next = current_node;
break; <<<=====
}
Seems like you meant return when you said break, now compiles with those replaced by return
Now this goes wrong
//linked list is empty
if (head == NULL) { //never enters if block for some reason
head->next = node_to_insert;
You just tested to see if head is NULL and if it is you try to use it, thats never going to work
You mean this
//linked list is empty
if (head == NULL) { //never enters if block for some reason
head = node_to_insert;
node_to_insert->next = NULL;
return;
}
code now runs to completion, although there may be other errors
node1's value equals 22
node2's value equals 33

Segfault and logic errors

I am making a linked list program which utilizes several functions to simplify adding or removing nodes from the list. I believe that my logic is alright with allocating and adding a new node but I am still getting a seg fault. Could you look over my logic and explain what is causing the seg fault? I am very inexperienced with linked lists. Thank you!
This is my function declarations
#define SIMPLELL_H
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
}node_t;
node_t* head;
void printList();
void append(int num);
void addFront(int num);
void deleteList();
void removeNode(int num);
int length();
#endif
This is the function definitions
void printList(){
// declare temp to traverse the LL and print each value
node_t *temporary = head;
while(temporary != NULL){
printf("%d, ", temporary->data);
temporary = temporary->next;
}
if (head == NULL){
printf("This list is empty.\n");
}
}
void append(int num){
//declare temporary pointer to traverse LL
node_t *tmp = head;
node_t *prev = NULL;
//malloc the new node
node_t *new = malloc(sizeof(node_t));
if(head == NULL){
//initialize the new node
new->data = num;
new->next = NULL;
head->next = new;
}
else{
//initialize the new node
new->data = num;
new->next = NULL;
//loop to traverse to the last value in the node
while(tmp != NULL){
prev = tmp;
tmp = tmp->next;
}
prev->next = new;
}
}
void addFront(int num){
//allocate memory for new node and allocate local variable equal to head
node_t *new = malloc(sizeof(node_t));
new->data = num;
new->next = head;
head = new;
}
void deleteList(){
node_t *tmp = head;
node_t *prev = NULL;
while(tmp != NULL){
prev = tmp;
tmp = tmp->next
free(prev);
}
}
void removeNode(int num){
//temp is used to traverse the ll until I find the node with the value
//then the previous value is linked to the next value removing the middle value.
node_t *temp = head;
node_t *prev = NULL;
if(head == NULL){
printf("List is not initialized\n");
return;
}
while(temp->data != num){
prev = temp;
temp = temp->next;
if (temp->data == num){
prev->next = temp->next;
free(prev);
}
else{
printf("Value is not in the list\n");
return;
}
}
int length(){
int length = 0;
node_t *temp = head;
while (temp != NULL){
length += 1;
temp = temp->next;
}
return length;
}
This is the driver function which I should not have to change
#include "SimpleLL.h"
#include "SimpleLL.c"
int main()
{
/*head is a global variable*/
head = NULL;
int size =0;
removeNode(1);
append(2);
append(3);
addFront(1);
append(4);
printList();
size = length();
printf("size now %d\n",size);
removeNode(8);
printList();
removeNode(1);
printList();
removeNode(4);
printList();
deleteList();
printList();
size = length();
printf("size after deletion %d\n",size);
return 0;
}
removeNode contains no test for reaching the end of the list. So if the value is not found in the list, it will dereference the NULL pointer at the end and crash.
This will be the case in the very first test, which attempts to remove a node when the list is empty, as well as for removeNode(8).
Your removeNode function also needs to free() the node it removed. Currently it is freeing the node that came after the one removed, which is still in the list and will cause undefined behavior when you access it later.

How to change the data of a node in Linked list in C?

struct:
//linked list
struct list
{
int32_t data;
struct list *next;
};
struct list *head;
at first, initialize linked list (every node be 0):
void initLinkedList(int size)
{
head = (struct list*)malloc(sizeof(struct list)*size);
struct list *current = head;
for(int i = 0; i < size; i++)
{
(current+i)->data= 0;
}
}
function:
void changeNode(int32_t element, int index)
{
struct list *current = head;
for (int i = 0; i < index; i++)
{
current = current->next;
}
current->data= element;
}
so basically, I want to create a linked list with size nodes, then change the data of the node through function.
But I'm getting Segmentation fault at this line in function:
current->localVar = element;
So, how can I change the data of a node without inserting a new one?
You must consider whether you have requested to change an index that doesn't exist in your list, but otherwise you are close. You are not iterating your initLinkedList in a traditional manner (you are treating the list like an array) As a side-note: In C, there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?.
Let's start with your initLinkedList function. You have declared head globally (assuming) and then go to fill your list with zeros. In doing so, you never initialize any of your ->next pointers. You don't end up with a linked-list -- you have a block of allocated nodes.
You should iterate current = current->next; and set each ->next pointer, not just use the offset +i. You are able to initialize your data like that due to allocating all nodes in a continual block -- but any node added later will not be contiguous in memory and none of your ->next pointers are initialized.
Now on to your changeNode function. You must fix your initLinkedList to initialize the ->next pointers. Then you need to change the return type from void to struct list* so you can return a pointer to the changed node on success or NULL on failure. Currently, you have no way of knowing if your change succeeded or failed.
Making the changes you could do:
struct list *changeNode (int32_t element, int index)
{
struct list *current = head;
for (int i = 0; i < index; i++) {
if (!current->next) {
fputs ("error: request exceeds no. of nodes in list.\n", stderr);
return NULL;
}
current = current->next;
}
current->data = element;
return current;
}
Look things over and let me know if you have further questions.
So, here void initLinkedList(int size) you aren't creating/initializing a linked list instead you are declaring an array of type struct list dynamically with size no. of elements and then you are initializing all of them with value 0.
To initialize your linked list code this:
void initLinkedList(int size)
{
if((head = (struct list*)malloc(sizeof(struct list)*size)) == NULL)
{
printf("Memory Unavailable.\n");
exit(1);
}
struct list *current = head;
for(int i = 0; i < size; i++)
{
current->data = 0;
if ((current->next = (struct list*)malloc(sizeof(struct list))) == NULL)
{
printf("Memory Unavailable.\n");
exit(2);
}
current = current->next;
}
current->next = NULL;
}
Then to changeNode, code this:
void changeNode(int element, int index)
{
struct list *current = head;
int count = 0;
while(count != index)
{
if(current->next)
current = current->next;
count++;
}
current->data = element;
}
Here I'm adding another function to print the linked list:
void print(struct list *head)
{
if(head->next)
{
printf("%d", head->data);
head = head->next;
print(head);
}
}
main():
int main(void)
{
initLinkedList(5);
changeNode(5, 4);
print(head);
delete(head);
printf("Memory freed.\n");
return 0;
}
Now you can see the Sample Output:
00005
Memory freed.
One more thing, if you are confused about this:
if ((current->next = (struct list*)malloc(sizeof(struct list))) == NULL)
I can help you. In the above line of code, we're allocating memory to current->next and checking if memory is allocated successfully or not.
Freeing the memory allocated to linked list using delete() function:
void delete( struct list *head )
{
struct list *current = head;
struct list *next = NULL;
while(current != NULL)
{
next = current->next;
free(current);
current = next;
}
}

Linked List in C: Accessing current->next is neither NULL or !NULL; problem with adding new nodes

So I encounter this problem where when I add nodes to an empty head in a specific sequence it doesn't work.
If I insert 2,1,3,4 so the second number is smaller than the first number, then it works. But if I add 2,3,4,5, then it doesn't work. I get a Segmentation fault (core dumped).
Can somebody tell me whats wrong here?
This is my struct and adding-method:
struct list_node{
int value;
struct list_node* next;
};
typedef struct list_node node_t;
node_t* add_element_sorted(node_t* head, int v){
node_t* current = head;
node_t* new = NULL;
new = (node_t *) malloc (sizeof(node_t));
if (new == NULL) {
printf("error");
exit(1);
}
new->value = v;
printf("%d\n", new->value);
if (current != NULL){ //check if first node is empty
while(current->value < v){
current = current->next;
printf("broke \n");
if(current->next == NULL){ // check if current node is the last then its next shoud be null
break;
}
}
}
if (current == head) {
new->next = head;
return new;
} else {
new->next = current->next;
current->next = new;
return head;
}
}
At this part it returns me current->next is neither NULL or !NULL. What is current->next now?
if(current->next == NULL){ // check if current node is the last then its next shoud be null
break;
}
My main looks like this:
int main(){
node_t* head = NULL;
head = add_element_sorted(head, 2);
head = add_element_sorted(head, 3);
head = add_element_sorted(head, 4);
head = add_element_sorted(head, 5);
return 0;
}

Linked list insertion behaviour

I found some odd behaviour in my C code last night.
I have a few base functions for creating and manipulating C Linked Lists.
The behaviour of my insert at nth position is odd though.
The first version works fine, but the second version does not insert at all into the list. Am I missing something?
//This works fine
void insert_nth(struct node** head, int data, int n_pos){
if (n_pos == 0){
insert_top(head, data);
return;
}
struct node* current = *head;
for(int i = 0; i < n_pos - 1 ; i++){
if(current == NULL) return;
else current = current->next;
}
if(current == NULL) return;
insert_top(&(current->next), data);
}
//This doesn't insert at all
void insert_nth(struct node** head, int data, int n_pos){
if (n_pos == 0){
insert_top(head, data);
return;
}
struct node* current = *head;
for(int i = 0; i < n_pos ; i++){
if(current == NULL) return;
else current = current->next;
}
if(current == NULL) return;
insert_top(&(current), data);
}
Here are the rest of the functions I'm using for reference.
int main(){
struct node* head = NULL;
build_rand_list(&head);
list_print(&head);
return 0;
}
void list_print(struct node** head){
printf("List size is %d: List: ", list_length(head));
for(struct node* current = *head; current!= NULL; current = current->next)
printf("%d ", current->data);
printf("\n");
}
void build_rand_list(struct node** head){
//Assume head is NULL
assert(*head == NULL);
srand(time(NULL));
for (int i = 0; i < 10; i++){
int random_num = rand() % 11;
insert_end(head, random_num);
}
}
void insert_top(struct node** head, int data){
struct node *new_node = (struct node *)malloc(sizeof(struct node));
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
&(current) is the address of a local variable.
&(current->next) is the address of a pointer inside a node in the list.
Modifying the local variable current, which is what insert_top ultimately does, has no effect on the list's nodes.
For example if you pass the node with the value 2 to the insert_top function the result will be something like this
It seems like you don't handle the pointers correctly. For example there is no node that points to the new node you created.
A better implementation would be
void insert_nth(struct node *head, int data, int npos) {
struct node *current = head;
for (int i = 0; i < npos - 1; i++) {
current = current->next;
if (current == null) {
printf("%s\n", "Insert failed");
return;
}
}
struct node *new_node = (struct node *)malloc(sizeof(struct node *));
new_node->data = data;
new_node->next = current->next;
current->next = new_node;
return;
}
Where the head parameter is the actual head of the list.
The result will then me more satisfying. Hope this helps.

Resources