Destroy a full linked list (c programming) - c

I am a beginner to C programming and am a bit stuck on pointers. i am trying to create a function that deletes all the elements of the linkes list. However my code deletes all elements except for the head.
I cannot alter the
void destroy(node *h)
parameters due to assignment title.
void destroy(set_element* head){
set_element* temp ;
set_element* curr = head;
if(head){
curr = head->next;
head->next = NULL;
while(curr !=NULL){
temp = curr->next;
free(curr);
curr = temp;
}
head =NULL;
}
}
thanks in advance.

The problem is this line
curr = head->next;
Here you make curr point to the next node. You need to make curr point directly to head.
A simpler version of the function might be e.g. this:
void destroy(set_element **head)
{
set_element *next;
for (set_element *curr = *head; curr; curr = next)
{
next = curr->next;
free(curr);
}
*head = NULL;
}
Note that I pass the head pointer by reference, otherwise the assignment to NULL will only change the local copy of the pointer.

If all you want to do is delete the entire linked list (head node included) and don't care whether the caller is left with a dangling pointer, this gets significantly simpler:
void destroy(set_element* head)
{
set_element* temp;
while (head)
{
temp = head;
head = head->next;
free(temp);
}
}
Invoked on the caller side as:
destroy(head);
That said, if you want to modify the callers passed-in pointer you cannot do it with this function signature. Like all other things in C (arrays not withstanding) parameters are passed by value, and if you need to modify the callers data, the parameter must be a pointer, and the passed-in value an address:
void destroy(set_element** headp)
{
set_element* temp;
while (*headp)
{
temp = *headp;
*headp = temp->next;
free(temp);
}
}
Invoked on the caller side as
destroy(&head);
Both of these assume your list is properly terminated with NULL.

Related

What happens to a temporary stack allocated pointer when used as a return value?

I'm doing some basic leetcode questions. Here I'm trying to swap pairs of a singly-linked list using recursion. The code below passes the tests but some point eludes me. new_head is a pointer created on the stack. I understand it means that once the function returns it is cleaned up and can potentially point to garbage. Is it correct to assume that here it works "by accident" and is not correct way to do it or is my understanding wrong?
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* swapPairs(struct ListNode* head){
if (head == NULL || head->next == NULL) {
return head;
}
struct ListNode* new_head;
new_head = head->next;
head->next = swapPairs(head->next->next);
new_head->next = head;
return new_head;
}
Another question related to the code above:
if I change the order of assignments I get a stack overflow but I can't wrap my head around the reason why
new_head = head->next;
new_head->next = head;
head->next = swapPairs(head->next->next);
Nothing that is touched in this line new_head->next = head; has an effect on what happens inside the recursion no (well it must have but I missing it)?
First Question
Return return new_head; does not return the object new_head to the caller. It returns the current value of new_head to the caller. That is fine.
Second Question
With:
new_head = head->next;
head->next = swapPairs(head->next->next);
new_head->next = head;
at the time swapPairs is called, the value passed to it, head->next->next, is the address of some node beyond head->next in the list.
With:
new_head = head->next;
new_head->next = head;
head->next = swapPairs(head->next->next);
at the time swapPairs is called, the value passed to it, head->next->next, is head, because new_head->next = head; just set head->next->next to head.

Reverse fuction for singly linked list isn't working as it should

we've given a task to reverse a singly linked list and for some reason i struggling with it
its reversing as it should ,but the head that should be the tail just disappears and i can't figure why, even after debugging
'''
void Reverse(struct node *head) {
struct node *last = NULL;
struct node *current = NULL;
struct node *temp = NULL;
current = head;
while (current->next != NULL) { //getting ptr to last item of the list
current = current->next;
last = current;
};
current = head; //resseting the current ptr back to the head of the list
while (current->next->next != NULL) { //getting the current ptr to one before the tail item
current = current->next;
};
temp = last;
while (last != head) {
if (current->next == last) {
last->next = current;
last = current;
current = head;
if (last == head) {
head->next = NULL;;
head->data = temp->data;
head->next = temp->next;
break;
};
};
};
};
'''
I can't completely follow the logic in your code. You find the last and the second last element, and I will assume that head is some dummy element that you always have (because otherwise, not checking if it is NULL is a problem). After that, I am not sure what happens. You "flip" the last two links if you are looking at the head you update it? (You don't need to set head->next to NULL before you update it two lines later; that doesn't do anything).
Did you want to move current from the head and down to the one before last somewhere in the loop? I think that would work, but you would get a quadratic time algorithm.
If we assume that head is a dummy, so it doesn't have any elements to worry about, and we just want its next pointer to point to the reversed links, you should be able to do something like this:
void reverse(struct node *head)
{
struct node *next = head->next;
head->next = 0;
while (next) {
struct node *next_next = next->next;
next->next = head->next;
head->next = next;
next = next_next;
}
}
In the while-loop you can think of the code as pushing next to the front of the list pointed to by head->next and popping it off the current list (by setting next to next_next. When you reach the end of the list, head->next points to all the links, in reversed order. (I haven't tested the code, but I belive it should work).
If head is not a dummy node, you have to handle the special case that it is NULL, and you have to move its value. It makes things trickier if you have to do the latter, especially if you want to use the list generically, where the user is allowed to allocate links, and you might not know what data is in them (beyond that they have a link embedded in them). I would go for having a dummy link if you can, it makes everything simpler, not just reversal.
Of course, with doubly-linked lists it gets simpler still:
#define swap_p(x,y) \
do { struct node *tmp = x; x = y; y = tmp; } while(0)
void reverse(node *dummy)
{
struct link *p = dummy;
do {
swap_p(p->prev, p->next);
p = p->prev;
} while (p != dummy);
}
but that might not be what you want.
Don't know if that helps, but I hope it does a little at least.

Deleting a whole linked list

I am working with linked lists and i fill the structure but when I delete the whole structure and try to print the contents of the linked list (should be null), a list of numbers appear. I know it is probably a memory problem. Any suggestions of how to fix it?
Code for deleting whole linked list:
void destroy(set_elem *head)
{
set_elem *current = head;
set_elem *next;
while (current != NULL)
{
next = current->next;
free(current);
current = next;
}
head = NULL;
}
While your delete function works correctly, it doesn't set the head in the caller to NULL when you do head = NULL; as you are only modifying a local pointer, which causes to your later logic where you are trying to print the values by checking the value of head.
To modify the original pointer, pass a pointer to head and set *head=NULL;
void destroy(set_elem **head)
{
set_elem *current = *head;
/* rest is the same */
*head = NULL;
}
when you copy head pointer passed into the function, head node is unaffected. You have to pass reference to the pointer to the head,then you will be able to delete head pointer. This can be also done by passing a pointer to the pointer to the head of the linked list but I find passing a reference more convenient. Following are the changes to your code.
void destroy(set_elem*& head)
{
set_elem* current = head;
set_elem* next;
while (current != NULL)
{
next = current->next;
free(current);
current = next;
}
*head = NULL;
}
You are not changing the original head. You should pass the pointer to this head i.e. pointer to a pointer or should return changed head to the caller. Following is the code to return changed head to the caller. There are other answers showing pointer to pointer approach.
set_elem* destroy(set_elem *head) {
set_elem *current = head;
set_elem *next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
head = NULL;
return head;
}
in caller,
head = destroy(head);
You're modifying only the local head pointer
Use a double pointer to modify the head, which would be later used for printing/processing
void destroy(set_elem** head)
{
set_elem* current = *head;
set_elem* next;
while (current != NULL)
{
next = current->next;
free(current);
current = next;
}
*head = NULL;
}

How to pop from linked list?

I've implemented a Linked-List with a Pop function in C:
Node * pop (Node * head) {
Node * temp = head;
printf("Temp is: %s\n", temp->val);
if (head->next != NULL) {
*head = *head->next;
}
printf("Temp is: %s\n", temp->val);
return temp;
}
And the output when I pop would be something like:
Temp is: node1 value
Temp is: node2 value
That is to say that temp is becoming temp->next when I assign *head = *head->next.
So how can I get the value of the current head and return it while also moving the head of the Linked-list to head->next?
Doing head = head->next does NOT remove the reference to the first node. (i.e. When I print the list, the first node is still there).
First, note that your code (and some of the previous solutions) will never pop the last element off the list. You want
if (*head != NULL) ...
Next, passing a pointer to a pointer will work. But it's actually better to make a list header like this:
typedef struct node_s {
struct node_s *next;
... data declaration here
} Node;
typedef struct list_s {
struct node_s *head;
} List;
void init_list(List *list) {
list->head = NULL;
}
Now declare a list like this:
List list[1];
init_list(list);
Declaring an array of one element makes every reference to list a pointer automatically, which eliminates lots of &'s in your code. Then it's nice and clean to implement push and pop:
void push(List *list, Node *node) {
node->next = list->head;
list->head = node;
}
Node *pop(List *list) {
Node *head = list->head;
if (head) {
list->head = head->next;
head->next = NULL;
}
return head;
}
Why is this better? Say you decide later to keep a count of items in the list. With the separate header node this is very easy:
typedef struct list_s {
struct node_s *head;
int length;
} List;
void init_list(List *list) {
list->head = NULL;
length = 0;
}
void push(List *list, Node *node) {
node->next = list->head;
list->head = node;
++list->length;
}
Node *pop(List *list) {
Node *head = list->head;
if (head) {
list->head = head->next;
head->next = NULL;
--list->length;
}
return head;
}
Note no calling code needs to change. With the pointer to pointer approach you are at a dead end. There are many other use cases where having a separate list header makes your code more flexible for future changes.
Your need to pass the address of head for your function to modify it. Then your function needs to dereference this address. Further, the last pop() needs to change *AddressOfHead as well
Node *pop(Node **AddressOfHead) {
Node *temp = *AddressOfHead;
if (temp) {
*AddressOfHead = temp->next;
}
return temp;
}
...
// Usage example
Node *TopOfList = pop(&Head);
Others have told you how to fix it, let me answer why temp changed..
Node * pop (Node * head) {
You are passing head as a pointer to a Node.
Thus when you do
*head = *head->next;
I think it is parsed as
*head = *(head->next);
And thus COPIES the object that is in next into the object at head, which is ofcourse the same object at temp.
Pointers are passed by value. That is, when you pass a pointer to the stack, a change in the called function to what the pointer points to is not reflected in the calling function.
In order for the value of the node pointer to be changed in the calling function, you need to pass the stack as a pointer to a pointer:
Node* pop (Node** head) {
Node* temp = *head;
if (temp) {
*head = temp->next; // to update stack in calling function
temp->next = NULL; // to detach temp from the rest of the list
}
return temp;
}
You do not need to check if ((*head)->next) or in this case if (temp->next) before updating the value of *head, because if you are at the last node of the stack and the next node is NULL, you want the list to be NULL anyway.
Karthik T's answer has the right explanation for why the value of temp was changing in your original code.
void pop(struct node** tol) {
struct node* t = *tol;
while (t->link->link != NULL){
t = t->link;
}
t->link = NULL;
}

Pointers to pointers: Difference between *a = b->c and a = &b->c

Looking back at the two star programming article I can't help but fail to see the significance of the difference between the following two lines:
*curr = entry->next;
curr = &entry->next;
The only difference I can see is that the first line changes *curr to point to the next node, and the second line makes a brand new ** pointing to the node member (which is the previous node in the next loop)
It occurs to me that freeing the entry in the first if block would stop the second line from working properly next loop, but in that case why not just use the first line in both cases? Is it a performance question?
Edit: Please read the second code block in the link above titled "Two star programming"
Edit: So I appear to have explained this poorly (Sorry!) so let me see if I can explain it a bit more verbose.
This is the source code in the article.
void remove_if(node ** head, remove_fn rm)
{
for (node** curr = head; *curr; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next;
free(entry);
}
else
curr = &entry->next;
}
}
By my reckoning the curr = &entry->next; line isn't necessary, you could use the other twice:
void remove_if(node ** head, remove_fn rm)
{
for (node** curr = head; *curr; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next;
free(entry);
}
else
*curr = entry->next;
}
}
Then you could move it above the if statement and save yourself a few lines:
void remove_if(node ** head, remove_fn rm)
{
for (node** curr = head; *curr; )
{
node * entry = *curr;
*curr = entry->next;
if (rm(entry))
{
free(entry);
}
}
}
In fact it looks like you don't need a pointer pointer at all and can do this:
void remove_if(node * head, remove_fn rm)
{
for (node* curr = head; curr; )
{
node * entry = curr;
curr = entry->next;
if (rm(entry))
{
free(entry);
}
}
}
So why did they do it the first way? Performance? Something else obscure?
The two are completely different.
curr = &entry->next;
takes the address of the variable entry->next, and assigns it to the pointer variable curr. After this assignment, whatever curr was previously pointing to will not have changed, but will have one fewer reference to it.
*curr = entry->next;
Does not change the value of curr at all, but changes the value of whatever it pointed to, which will have the same number of references as before but a different value.
Yes, both of these will have as one effect that *curr will be equal to entry->next, but they in fact write different values to different memory locations and have other side effects that matter.
The code in the article is correct, and all three of your proposed variants are not correct. Let's look at the correct code first, with some annotations:
void remove_if(node ** head, remove_fn rm)
{
// node ** passed to allow use to modify caller's head pointer
for (node** curr = head; *curr; )
{
// curr is a local variable, that points to a node pointer
// curr points either to the caller's head pointer, or to
// a next pointer within the list
node * entry = *curr;
if (rm(entry))
{
// remove this entry, which means modifying the list
*curr = entry->next;
// modify *curr modifies either caller's head pointer
// or a next pointer
free(entry);
}
else
// did not remove, so do not modify the list
curr = &entry->next;
}
}
The key points to note here:
*curr = entry->next modifies the list.
curr = &entry->next does not modify the list.
Your first two proposed versions are wrong because they do
*curr = entry->next
at every iteration. Assigning to *curr modifies the list. Imagine a scenario where rm() always returned false. In that case you must never modify the list, but you modify it every time round the loop.
And the final variant is wrong in because you are only passing in a node * then there's no way for the caller's head pointer to be modified. In fact, the final variant does not modify the list at all.

Resources