Deleting a node from a doubly linked list - c

This is my current code which i have messed with so how im dealing with the first item or only item are wrong, which are the first 2 parts to this function. For some reason i am getting memory errors if i just try to set node=node->next_.. i would assume this would be the easiest way but when i throw that back into the program i start getting memory access issues. all other parts work fine as long as i don't manipulate the head address.
void removeNode(struct student_record_node* node)
{
struct student_record_node *temp=NULL;
temp=node;
if(node->next_==NULL&&node->prev_==NULL)
{
node=node->next_
free(node->prev_);
}
else if(node->prev_==NULL&& node->next_!=NULL)
{
node=node->next_
free(node->prev_);
}
else if(node->next_!=NULL && node->prev_!=NULL)
{
node->prev_->next_ = node->next_;
node->next_->prev_ = node->prev_;
student_record_node_deallocate(node);
}
else if(node->prev_!=NULL&& node->next_==NULL)
{
node->prev_->next_=node->next_;
student_record_node_deallocate(node);
}
}

There are several errors:
The node may be the head, so struct student_record_node* should be returned, instead of void.
node may be NULL
if node->prev_ is NULL, make sure to free the node deleted.

Since you did not provide, what your linked list looks like, I am going to assume for my code-example, that it is a struct containing a pointer to the head of the linked list (the first element). It is referred to by your_list.
The problem lies within the first two if-blocks in your code;
The first if: if(!node->next_ && !node->prev_):
This means you are deleting the head-element of the list. In this case, you will have to explicitly set the head to NULL, instead of setting the pointer to the node you wish to delete to NULL (by setting it to its predecessor, which is NULL). Also, you are freeing a NULL-Pointer, by freeing the previous node. This is in it self not a problem, but you want to delete node, not it's predecessor.
The second if: if(!node->prev_ && node->next_):
This means you are deleting the head, but the list will not be empty after the node is deleted. In this case, you must set the head of the list to point to the new head, which will be the node pointed to by node->next_. Also, you have the similar problem with free() as before.
Addressing those two points, your code should do something along the lines of this:
void removeNode(struct student_record_node *node){
if(!node->next_ && !node->prev_){
your_list->head = NULL; // Remove head - List is now empty.
student_record_node_deallocate(node);
node = NULL; // Set freed pointer to NULL, for safety.
}
else if(!node->prev_ && node->next_){
your_list->head = node->next_; // Set the head to the new head.
student_record_node_deallocate(node);
node = NULL; // Set freed pointer to NULL, for safety.
}
else if(node->next_ && node->prev_){
node->prev_->next_ = node->next_;
node->next_->prev_ = node->prev_;
student_record_node_deallocate(node);
node = NULL; // Set freed pointer to NULL, for safety.
}
else if(node->prev_ && !node->next_){
node->prev_->next_ = NULL;
student_record_node_dealocate(node);
node = NULL; // Set freed pointer to NULL, for safety.
}
}

Related

Delete node from singly linked list and do an operation on it

I am tasked with removing a node from a singly linked list and setting the structure that the node's dataPtr points to, to a new value. I create a structure pointer to hold the data of the popped node. There are 2 cases I want to catch 1) in which this new pointer is null, I want to set it to the popped node 2) if the pointer is not null, I want to do some operations on it.
NODE* printer;
printer = (NODE*) malloc(sizeof (NODE)); //dynamically allocate
if(printer==NULL){ //if it has no data
printer= deleteNode(sList); //call deleteNode function which returns popped node from the passed singly linked list
} else if (printer!=NULL && sList->count!=0) { //if it has data
(((PRINTJOB *) printer->dataPtr)->pageNums) -= PAGESPERMINUTE; //decrement the pageNums field by 1 (PAGESPERMINUTE)
if ((((PRINTJOB *) printer->dataPtr)->pageNums) <= 0) { //if the field is less than 0
printer = NULL; //set pointer back to null
}
printf("printers pageNum is: %d\n", ((PRINTJOB *) printer->dataPtr)->pageNums);
}
My compiler is giving me an error on 4th line: The value is never used.
It is also giving me an error in my else if statement: first condition is always true.
When I run this code block as well, it crashes my program.
My deleteNode function is:
#include "headers.h"
void* deleteNode(LIST* list){
NODE *toDelete;
toDelete = list->head;
list->head = toDelete->next;
return toDelete;
}
my NODE structure is:
typedef struct node{
void* dataPtr;
struct node* next;
} NODE;
I am tasked with removing a node from a singly linked list and setting the structure that the node's dataPtr points to, to a new value.
But you remove the node only conditionally (and on a condition that is unlikely to actually occur). If, as stated, the first step is to remove a node then Remove. That. Node.
I create a structure pointer to hold the data of the popped node.
But you shouldn't. If there is any data available to receive then that's because a node containing it already exists, and your deleteNode() function will return a pointer to it (provided that function is in fact called).
There are 2 cases I want to catch 1) in which this new pointer is null, I want to set it to the popped node
That makes no sense, because it makes no sense to create a new, separate node in the first place. What would make sense would be to check whether deleteNode returns a null pointer, which one imagines it might do if the list were empty (but see below).
if the pointer is not null, I want to do some operations on it.
That could make sense, but not in this context. According to your description, you want to perform operations on the node that was removed from the list (provided that one in fact was removed), but instead you are working on the newly-allocated, uninitialized node.
Based only on your description of the task itself, it sounds like you want something more like this:
NODE* printer = deleteNode(sList);
if (printer != NULL) {
(((PRINTJOB *) printer->dataPtr)->pageNums) -= PAGESPERMINUTE;
if ((((PRINTJOB *) printer->dataPtr)->pageNums) <= 0) {
printer = NULL; //set pointer back to null (?)
}
printf("printers pageNum is: %d\n", ((PRINTJOB *) printer->dataPtr)->pageNums);
} // else nothing to do
But there are other possibilities, depending on how the list is structured and used.
Note that the printer = NULL; line that I copied from your original code is questionable. It may make sense if later code performs a null check on printer before doing yet more processing, and you want to circumvent that. Beware, however, that failing to first free() the node might constitute a memory leak. It looks suspicious in that way, but it is possible that the node really shouldn't be freed there.
Note also, however, that your deleteNode() function appears to be likely to break when it operates on an empty list. In that event, it seems like the only sensible thing it could return is a null pointer. It might well be that list->head is in fact such a pointer in that case, but then
NODE *toDelete;
toDelete = list->head;
list->head = toDelete->next;
will attempt to dereference that null pointer when it evaluates toDelete->next, thus reaping undefined behavior. If in fact you can rely on list->head to be null when the list is empty, then you would want to modify the above something like this:
NODE *toDelete;
toDelete = list->head;
if (toDelete != NULL) {
list->head = toDelete->next;
} // else list->head is already NULL
Again, there are other possibilities depending on how the list is structured and used, but I think the above is probably what you want.

Can't find the segmentation fault in my code

#include <stdio.h>
#include <stdlib.h>
typedef struct node{
struct node* next;
int value;
}Node;
int findLastNodeValue(Node* head){
while(head -> next != NULL){
head = head -> next;
}
return head -> value;
}
int main(){
Node *node1,node2;
node1 = (Node *)malloc(sizeof(Node);
node2 = NULL;
node1 -> next = node2;
findLastNodeValue(node1);
findLastNodeValue(node2);
return 0;
}
This code giving a segmentation fault. But I cannot find why does it occur. Can you help me with this one.
There are multiple issues with your code:
You are mallocing node1 yet you are not setting the value anywhere. This creates undefined behaviour whenever you try to access value - you might crash the program or you might get garbage data, which is usually worse since it then leads to other parts of your code acting weird.
You are not freeing the dynamically allocated memory. While in your case it's not such big of a deal it tells me that you are not familiar with the way dynamic allocation works (my belief is also strengthened by the first bullet point in this list). Whenever you malloc something, always free it (in C++ you have new and delete) and (to prevent unexpected behaviour) set the pointer to NULL.
node2 is not a pointer. The * in Node *node1, node2; applies only to the first variable. Each consecutive variable also needs a * otherwise it will be allocated on the stack.
By looking at your code it's clear that you want node2 to be a pointer (otherwise you wouldn't be assigning NULL as its value :)). In that case you are trying to access next of node2 but node2 is initialized to NULL:
int findLastNodeValue(Node* head){ // You are passing node2, which is NULL
while(head -> next != NULL){ // Can't access next of a NULL -> code breaks
head = head -> next;
}
return head -> value;
}
As a general rule do the following:
Try using a function that instantiates your node - since this is C you don't have a constructor I would suggest to write a function (or several depending on how much functionality you need) that generates a new node. This way you will ensure that at least there is no chance of not initializing a node
For example:
Node* createNode(int value) {
Node* node = (Node *)malloc(sizeof(Node));
if (!node) return NULL; // If malloc fails for some reason
node -> value = value;
node -> next = NULL;
return node;
}
Try using a function that deletes your node - if there is any chance of accessing a deleted reference again, set it to NULL and handle the NULL value accordingly
For example:
void deleteNode(Node** node) {
if (!*node) return;
free(*node);
*node = NULL;
}
Note that the code above doesn't delete what's reference by next since we only want to delete the node we are passing to the function. If you have previous (in the case of a double linked list), you will have to first access the next node, set its previous value to NULL and then delete your current node.
Whenever you pass a pointer, always check if that pointer is NULL before you do anything with the data that it's supposedly referencing. Combining this with the node creator function from the first point you can be certain that you are not passing some Node pointer that has not been initialized properly
Now as to your function in particular I would do the following:
int findLastNodeValue(Node* head) {
if (!head) return -1; // We have a null reference, so there is nothing else to do here; exit accordingly and check the return value to see if the function call has been successful
while(head -> next != NULL) {
head = head -> next;
}
return head -> value;
}
findLastNodeValue(node2) is your biggest problem. When you send NULL into findLastNodeValue, the very first thing you try to do is dereference the NULL pointer at the clause (head -> next != NULL).
To resolve this, you can check for and handle the case when head is NULL in your findLastNodeValue function before your while loop.

difference between (*head)->next and &(*head)->next in C [LINKED LIST]

typedef struct n {
int data;
struct n *next;
}node;
This function deletes all nodes with odd values (without freeing memory or additional variables) :
void deleteOdds (node **head) {
if (*head == NULL) {
return;
}
while ((*head)->next) {
if ((*head)->data % 2 != 0) {
*head = (*head)->next;
} else head = &(*head)->next;
}
}
I understand the logic (and already have some assumptions), but I'm not sure how to explain the difference between
*head =(*head)->next;
and
head = &(*head)->next;
thanks in advance!
The key to understanding what's going on is to see what is being assigned in each case:
*head = (*head)->next; assigns to whatever head is pointing to, which is either the original head pointer, the pointer to which is passed to the function, or a next of some prior node. We modify what is pointed to, while the pointer stays the same. This amounts to deleting from the list (and creating a memory leak in the process).
head = &(*head)->next assigns to the head itself, i.e. it modifies the pointer, not the thing that it points to. This amounts to skipping ahead in the list, without modifying it.
Note: head is not the ideal name for the variable. Since the variable points to head pointer only until the first "skip", a better name for it would be current.

Delete Linked List Function

Here's my function to delete a linked list:
void deleteList( NODE* head )
{
NODE* temp1;
NODE* tempNext;
temp1 = head;
tempNext = NULL;
while( temp1 != NULL )
{
tempNext = temp1->next;
free(temp1);
temp1 = tempNext;
}
}
So temp1 first points where the head pointer is pointing. If it isn't NULL, tempNext will be set to point to the next element of the list. Then the first element (temp1) is free'd, and temp1 is reassigned to point to where tempNext is pointing and process repeats.
Is this the right approach to deleting an entire list?
I ask this because when I print the list after using this function, it still prints the list. And IIRC freeing something doesn't delete it but only marks it as available so I'm not sure how to tell if this is correct or not.
Your code looks correct.
You're also correct that freeing a list's elements doesn't immediately change the memory they pointed to. It just returns the memory to the heap manager which may reallocate it in future.
If you want to make sure that client code doesn't continue to use a freed list, you could change deleteList to also NULL their NODE pointer:
void deleteList( NODE** head )
{
NODE* temp1 = *head;
/* your code as before */
*head = NULL;
}
It still print the list, because you probably don't set the head pointer to NULL after calling this function.
I ask this because when I print the list after using this function, it still prints the list.
There is a difference between freeing a pointer and invalidating a pointer. If you free your whole linked list and the head, it means that you no longer "own" the memory at the locations that head and all the next pointers point to. Thus you can't garintee what values will be there, or that the memory is valid.
However, the odds are pretty good that if you don't touch anything after freeing your linked list, you'll still be able to traverse it and print the values.
struct node{
int i;
struct node * next;
};
...
struct node * head = NULL;
head = malloc(sizeof(struct node));
head->i = 5;
head->next = NULL;
free(head);
printf("%d\n", head->i); // The odds are pretty good you'll see "5" here
You should always free your pointer, then directly set it to NULL because in the above code, while the comment is true. It's also dangerous to make any assumptions about how head will react/contain after you've called free().
This is a pretty old question, but maybe it'll help someone performing a search on the topic.
This is what I recently wrote to completely delete a singly-linked list. I see a lot of people who have heartburn over recursive algorithms involving large lists, for fear of running out of stack space. So here is an iterative version.
Just pass in the "head" pointer and the function takes care of the rest...
struct Node {
int i;
struct Node *next;
};
void DeleteList(struct Node *Head) {
struct Node *p_ptr;
p_ptr = Head;
while (p_ptr->next != NULL) {
p_ptr = p_ptr->next;
Head->next = p_ptr->next;
free(p_ptr);
p_ptr = Head;
}
free(p_ptr);
}

Pointers in c: Function which deletes every second element of linked list

I want to write a function which gets a pointer to a header of a linked list and deletes from the list every second member of it. The List is a linked elements of type element:
typedef struct element{
int num;
struct element* next;
}element;
I'm new to all these pointers arithmetic so I'm not sure I write it correctly:
void deletdscnds(element* head) {
element* curr;
head=head->next; //Skipping the dummy head//
while (head!=NULL) {
if (head->next==NULL)
return;
else {
curr=head;
head=head->next->next; //worst case I'll reach NULL and not a next of a null//
curr->next=head;
}
}
}
I kept changing it since I kept finding errors. Can you please point out any possible errors?
The algorithm is a lot simpler if you think of your linked list in terms of node pairs. Each iteration of your loop should process two nodes - head and head->next, and leave head equal to head->next->next upon exit. It is also important to not forget deleting the middle node, if you are cutting it out of the list, otherwise you are going to see memory leaks.
while (head && head->next) {
// Store a pointer to the item we're about to cut out
element *tmp = head->next;
// Skip the item we're cutting out
head->next = head->next->next;
// Prepare the head for the next iteration
head = head->next;
// Free the item that's no longer in the list
free(tmp);
}
It might be most straightforward to visualize this problem in recursive terms, like this:
// outside code calls this function; the other functions are considered private
void deletdscnds(element* head) {
delete_odd(head);
}
// for odd-numbered nodes; this won't delete the current node
void delete_odd(element* node) {
if (node == NULL)
return; // stop at the end of the list
// point this node to the node two after, if such a node exists
node->next = delete_even(node->next);
}
// for even-numbered nodes; this WILL delete the current node
void delete_even(element* node) {
if (node == NULL)
return NULL; // stop at the end of the list
// get the next node before you free the current one, so you avoid
// accessing memory that has already been freed
element* next = node->next;
// free the current node, that it's not needed anymore
free(node);
// repeat the process beginning with the next node
delete_odd(next);
// since the current node is now deleted, the previous node needs
// to know what the next node is so it can link up with it
return next;
}
For me, at least, this helps clarify what needs to be done at each step.
I wouldn't advise actually using this method because, in C, recursive algorithms may take up a lot of RAM and cause stack overflows with compilers that don't optimize them. Rather, dasblinkenlight's answer has the code that you should actually use.

Resources