Deleting first node in linked list in C - c

I know that this was answered many times, but I just can't figure out what is wrong.
I have a structure
typedef struct zaznam{
char kategoria[53];
char znacka[53];
char predajca[103];
double cena;
int rok;
char stav[203];
struct zaznam *next;
}ZAZNAM;
And I want to delete first node this way.
for(prev = act = prvy; act!=NULL; prev=act, act=act->next){
if((strstr(act->znacka,vyber)!=NULL)){
if(act->znacka==prvy->znacka){
//if "znacka" of the actual node is equal to the first
prev=prvy->next; //in "previous" is pointer to the second node
free((void *)act); //free first node
prvy=prev; //first node = previous
}
else{ //this works
prev->next=act->next;
free((void *)act);
act=prev;
}
And it works for everything but not for the first node.

Well, you violate your own invariant prev->next == act already in the beginning: for (prev = act = prvy;...
Second, this act->znacka==prvy->znacka should be act==prvy to find out whether you are at the beginning of the chain, otherwise it confuses people.
And I would probably try to reestablish you starting (but wrong) invariant (which is act==prev) by adding act=prev; for the first case. Maybe it will work.

Looks to me like when you delete the first node you're not properly assigning prev to point to the next node when you continue on the next iteration of the for-loop. For instance, in your first if statement to check if you're at the first node, you do the following:
prev=prvy->next; //in "previous" is pointer to the second node
free((void *)act); //free first node
prvy=prev; //first node = previous
That does free the first node, but, immediately in your for-loop you then assign act to prev here:
for(prev = act = prvy; act!=NULL; prev=act, act=act->next)
Since freeing memory doesn't actually erase the contents of memory, you proceed down the rest of the list as if the first node had not been freed. Actually using a freed pointer like that is undefined behavior, so anything could happen (i.e. you could crash, etc. ... in this case it seems like you aren't).
Try changing your code to the following:
for(prev = act = prvy; act!=NULL;)
{
if((strstr(act->znacka,vyber)!=NULL))
{
if(act==prvy)
{
//we're at the first node
ZAZNAM* temp=act->next;
free((void *)act); //free first node
prev=act=prvy=temp; //re-establish starting condition
}
else
{
prev->next=act->next;
free((void *)act);
act=prev->next;
}
}
else
{
//iterate here because if you delete the first node, you don't want
//to start iterating like would happen if you kept iteration in the
//for-loop declaration
prev=act;
act=act->next;
}
}

why do you have a loop. Deleting the first entry in a list is just a matter of
tmp = head->next
delete head
head = tmp

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.

Issue removing a node from a linked list

Since the code was cryptic I decided to reformulate it.
This code is trying to remove the second element from a linked list (the node with number 2 on "int data"). The first parameter of remove_node is the address of the first pointer of the list, so if I have to remove the first pointer of the list I am able to start the list from the next pointer.
The problem is inside the second while loop, inside the if clause, more specifically with the free(previous->next) function, it is changing the address pointed by address_of_ptr (aka *address_of_ptr) and I can't understand why this is happening. Could someone enlighten me?
#include <stdlib.h>
typedef struct node
{
struct node *next;
int data;
} node;
void remove_node(node **address_of_ptr, int int_to_remove)
{
node *previous;
while (*address_of_ptr != NULL && (*address_of_ptr)->data == int_to_remove)
{
previous = *address_of_ptr;
*address_of_ptr = (*address_of_ptr)->next;
free(previous);
}
previous = *address_of_ptr;
address_of_ptr = &((*address_of_ptr)->next);
while (*address_of_ptr != NULL)
{
if ((*address_of_ptr)->data == int_to_remove)
{
address_of_ptr = &((*address_of_ptr)->next);
free(previous->next);
previous->next = *address_of_ptr;
/*or
previous->next = (*address_of_pointer)->next;
free(*address_of_pointer);
address_of_pointer = &previous->next;
*/
}
else
{
previous = *address_of_ptr;
address_of_ptr = &((*address_of_ptr)->next);
}
}
}
int main(void)
{
node *head = malloc(sizeof(node));
node *a = malloc(sizeof(node));
node *b = malloc(sizeof(node));
head->next = a;
a->next = b;
b->next = NULL;
head->data = 1;
a->data = 2;
b->data = 1;
remove_node(&head, 2);
return 0;
}
I figured it out using pythontutor.com.
Here is an image of the execution about to replace address_of_ptr with the address of the next pointer in the list:
about to replace address_of_ptr
In my mind what would happen is that address_of_ptr would move from head to a:
like this
But what actually happens is this:
address_of_ptr move from head to the first node's next variable
Which is also the address of the next node (such as a).
Inside the if clause:
address_of_ptr is set to the second node's next variable
What I was expecting was it being set to b:
Like this
Since it becomes equivalent to previous->next, it causes program to free address_of_ptr.
I would like to thank everyone who helped me sorting this issue, I truly appreciate your effort in making the explanation clear as possible. After a lot of thinking a solution that I believe is much simpler than my first code come up. #David C. Rankin mentioned "Linus on Understanding Pointers", and I think "I might have grasped" what Linus was referring to:
void remove_node(node **address_of_ptr, int int_to_remove)
{
node *to_remove;
while (*address_of_ptr != NULL)
{
if ((*address_of_ptr)->data == int_to_remove)
{
to_remove = *address_of_ptr;
*address_of_ptr = (*address_of_ptr)->next;
free(to_remove);
}
else
{
address_of_ptr = &(*address_of_ptr)->next;
}
}
}
First of all, if the first while loop stops due to *address_of_ptr == NULL, then the line
address_of_ptr = &((*address_of_ptr)->next);
will cause undefined behavior, due to deferencing a NULL pointer. However, this will not happen with the sample input provided by the function main, so it is not the cause of your problem.
Your problem is the following:
When first executing inside the if block of the second while loop (which will be in the first iteration of the second while loop with your sample input), address_of_ptr will point to the next member of the first node in the linked list, so the pointer will have the value of &head->next. This is because you have the line
address_of_ptr = &((*address_of_ptr)->next);
in several places in your program, and it will execute one of these lines exactly once by the time you enter the if block.
The first line of that if block is also the line
address_of_ptr = &((*address_of_ptr)->next);
so that after executing that line, address_of_ptr will point to the next member of the second node in the linked list, so the pointer will have the value of &a->next.
At that time, the value of previous will be head, because at the time
previous = *address_of_ptr;
was executed, address_of_ptr had the value of &head.
Therefore, when the next line
free(previous->next);
is executed, previous->next will have the value of a, which ends the lifetime of that node. As already stated, at this time address_of_ptr will have the value &a->next, which means that address_of_ptr is now a dangling pointer, because it is now pointing to a freed memory location. Therefore, it is not surprising that *address_of_ptr changes after the free statement, because freed memory can change at any time.
Note that the function remove_node can be implemented in a much simpler way:
void remove_node( node **pp_head, int int_to_remove )
{
node **pp = pp_head, *p;
//process one node per loop iteration
while ( (p = *pp) != NULL )
{
//check if node should be removed
if ( p->data == int_to_remove )
{
//unlink the node from the linked list
*pp = p->next;
//free the unlinked node
free( p );
}
else
{
//go to next node in the list
pp = &p->next;
}
}
}
This line:
begin_list = &previous->next;
You might be mis-thinking how pointers to pointers work. Since begin_list is a t_list **, you generally want to assign to *begin_list. Also, since previous->next is already an address, you don't want to assign the address to *begin_list, just the value, so this should work instead:
*begin_list = previous->next;
Same for the similar line below it.
I prepared an answer last night several minutes after you asked the question at first, and put a lot of thought into it, but didn't come back until just now. I'd let my version go, but it's a more detailed look into the problem anyway, so I thought I'd include it for posterity.
The problem lay on line 36:
begin_list = &(*begin_list)->next;
In the first while loop in the function ft_list_remove_if, I can see that you wanted to change the beginning of the list to point to some list item potentially later in the list; so for example, if you wanted to remove all items in the list which contained the string "a", this function should indeed change the value of list1 in the main function; this way the new start of the list would actually be list2.
However, by the time the second while loop is entered, there's no need to change the pointer to the start of the list anymore; that was taken care of in the first while loop. As such, begin_list should really not be used anymore; it is a pointer to a pointer to a list item, so it references the variable which points to a list item. This is not desirable. Let's step through the program line-by-line and I'll show you what I mean.
Upon entering the function ft_list_remove_if, we run past the first while loop since the first element of the list does not contain the data "b":
while (*begin_list != 0 && (*cmp)((*begin_list)->data, data_ref) == 0)
{
[...]
}
We're still on the first list item when we enter the second while loop, and the first item does not match. We therefore go to the else branch of the if statement on line 25:
if ((*cmp)((*begin_list)->data, data_ref) == 0)
{
[...]
}
else
{
previous = *begin_list;
begin_list = &(*begin_list)->next;
}
In the else branch, previous is set to the first item in the linked list. Fair enough. Then begin_list is set to the address of the next member of the first list item. This means that when the first list item's next member changes, so does the value of *begin_list. Let that point sink in.
Now we go on to the next iteration of the while loop. This time *begin_list points to the second list item, which does indeed match the search criteria, so we enter the main if branch instead of the else:
if ((*cmp)((*begin_list)->data, data_ref) == 0)
{
previous->next = (*begin_list)->next;
free(*begin_list);
begin_list = &previous->next;
}
OK. So let's remember here: previous is set to the first item in the linked list. *begin_list is set to the second item in the linked list. But what is begin_list set to? In short, the address of previous->next. Remember the following from the last cycle of the loop?
previous = *begin_list;
begin_list = &(*begin_list)->next;
So now when previous->next is changed on line 28 to the third item in the list, *begin_list is also changed to the third item in the list! That means that line 29 frees the memory allocated to the third item in the list, while the second item no longer referenced by any pointers! By sheer luck, the program might continue to look like it functions properly for the next few steps, but it doesn't; the third element has been freed, and the contents of the third element get scrambled up a little bit and the third element's next and data members are filled with invalid data, resulting in the segfault.
However, the procedure worked when you copied *begin_list to to_remove first, and then freed the data pointed to by to_remove:
to_remove = *begin_list;
previous->next = (*begin_list)->next;
free(to_remove);
begin_list = &previous->next;
This is because to_remove continues to point to the second element, even after *begin_list has been changed by the previous->next = (*begin_list)->next; statement; thus the second element is freed as it should be, and on line 31 begin_list is set to the address of the third element, just like it should be.
That doesn't mean that the latter is a good solution. Really, I think begin_list shouldn't be used at all beyond the first while loop, since it doesn't have to be. My suggestion is to use another pointer named current with which you can step through the list:
#include <stdlib.h>
#include <stdio.h>
typedef struct s_list
{
struct s_list *next;
void *data;
} t_list;
void ft_list_remove_if(t_list **begin_list, void *data_ref, int (*cmp)())
{
t_list *previous;
t_list *current;
if (begin_list == 0)
return;
while (*begin_list != 0 && (*cmp)((*begin_list)->data, data_ref) == 0)
{
previous = *begin_list;
*begin_list = (*begin_list)->next;
free(previous);
}
current = *begin_list;
while (current != 0)
{
if ((*cmp)(current->data, data_ref) == 0)
{
previous->next = current->next;
free(current);
current = previous->next;
}
else
{
previous = current;
current = current->next;
}
}
}
int ft_strcmp(char *s1, char *s2)
{
int i;
i = 0;
while (s1[i] == s2[i] && s1[i] != '\0')
++i;
return (s1[i] - s2[i]);
}
int main(void)
{
t_list *list1 = malloc(sizeof(*list1));
t_list *list2 = malloc(sizeof(*list2));
t_list *list3 = malloc(sizeof(*list3));
list1->data = "a";
list2->data = "b";
list3->data = "a";
list1->next = list2;
list2->next = list3;
list3->next = 0;
ft_list_remove_if(&list1, "b", &ft_strcmp);
return (0);
}

Deleting a node from a doubly linked list

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.
}
}

C - Linked Lists

I am trying to understand the code of linked lists. I understand how they work.
I am looking at some code to do with dynamic memory and linked lists, I have simplified it here:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
char *word;
struct node *next;
} node;
void display_word(node *start) {
node *start_node = start;
puts("");
for(; start_node != NULL; start_node = start_node->next) {
printf("%s", start_node->word);
}
}
node* create_node(char *input) {
node *n = malloc(sizeof(node));;
n->word = strdup(input);
n->next = NULL;
return n;
}
int main() {
node *start_node = NULL;
node *n = NULL;
node *next_node = NULL;
char word_holder[20];
for(; fgets(word_holder,80,stdin) != NULL; n = next_node) {
next_node = create_node(word_holder);
if(start_node == NULL)
start_node = next_node;
if(n != NULL)
n->next = next_node;
}
display_word(start);
}
So the program creates a linked list of each word the user enters and then it prints it out.
What I dont understand is in the main() function where next_node is assigned to a new node everytime to create a new one, but start_node points to next_node, so it will point to every new node that next_node creates each time? So how is it possible to still keep the list? Shouldn't we lose the old node each time?
Can someone explain please.
When the first node is created, a pointer to it is saved in start.
When subsequent nodes are created, they are added at the end of the list, so start still points to the first node, and through it, the rest of the list.
Step through the code with a debugger, or get out a pencil and paper and draw what's happening as you step through in your brain, and you'll see how it all gets put together.
When the first node is created, a pointer to it is saved in start.
After every iteration of the loop, "n" is set to the node just created, because the last piece of the for loop (;n = next) is executed after every iteration of the loop. So mid loop execution "n" will always be pointing to the previous node. Therefore the statement n->next = next is setting the previous node's "next" pointer to the new node.
So during the second iteration of the loop, n = start, and start->next is set to "next" the node you just created.
I hope this answers your question - every time you are updating "next", you're setting that to be yet another new node. Each node has their own "next" that leads to the next node, so you aren't going to lose anything by doing it this way. I didn't actually test your code but since "Start" points to the first node always, you aren't going to lose any nodes along the way. A debugger should help if you're curious to learn more about how this works!

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