How do I delete a doubly linked list in C? - c

I've created a doubly linked list, filled it with values and now I want to delete it and remove all the values to avoid memory leaks. Here's what I wrote as well as the structs that were used when creating the doubly linked list. Both those functions will be called towards the end of the main function.
struct node
{
struct node *next;
struct node *prev;
char *value;
};
// The type for a list.
typedef struct list
{
struct node head;
} List;
// The type for a list position.
typedef struct list_pos
{
struct node *node;
} ListPos;
void list_destroy(List *lst)
{
List p,q;
p = *lst;
while (p)
{
q = p.head->next;
free(p);
p = q;
}
*lst = NULL;
}
// Remove the value at the position and return the position of the next element.
ListPos list_remove(ListPos pos)
{
}

You appear to have the right general idea: you walk the list and free each node, making sure to grab any needed data from each node (in particular, the pointer to the next node) while the node holding it still exists. Your case differs from some that you might have seen, however, because instead of handling the overall list via a bare pointer to the head node, you have a separate object, of a separate type (List / struct list), to represent the list itself. This approach has much to recommend it, including, especially, the use of (apparently) a dummy head node, which provides for a variety of algorithmic simplifications. This is usually how I write a linked list.
But because struct list is not struct node, you cannot set a list pointer equal to a node pointer. Instead, create a struct node * to track your position. The first node to free would be the one referenced by struct node *to_free = lst->head.next, and the one after that would be the one referenced by to_free->next.
Note that you might need to free the struct list, too.

Related

Sentinel Node in a C Linked List

I'm trying to learn more about linked lists in C and I recently stumbled upon the Sentinel Node concept, but I can't wrap my head around it. According to the slides I have, the sentinel node should be the first thing on the list when it's created and the last when other nodes are added. There should be a pointer to permanently point to the Sentinel Node.
All those stuff confuse me and I would love some help with the implementation.
/*this is a simple LL implementation*/
#include <stdio.h>
#include <stdlib.h>
struct List
{
int data;
struct List *next;
};
void ListInsert(int new_data)
{
struct List *p;
p = (struct List *)malloc(sizeof(struct List));
p->data = new_data;
p->next = (head);
head = p;
}
void printList(struct List *q)
{
q = head;
while (q != NULL)
{
printf("%d ", q->data);
q = q->next;
}
printf("\n");
}
int main()
{
ListInsert(5);
ListInsert(7);
ListInsert(6);
ListInsert(4);
ListInsert(2);
printList(head);
return 0;
}
Now, if I want to create the sentinel node, how should I proceed?
According to the slides i have, the sentinel node should be the first
thing on the list when its created and the last when other nodes are
added.There should be a pointer to permanently point to the Sentinel
Node.
Let's start with the most important point: the purpose of a sentinel node, which is to mark the end of the list. There will not be real data associated with a sentinel node, so a list containing only a sentinel node is logically empty.
A few things follow from that, including:
the identity of the sentinel node is a property of the whole list, not of any (other) particular node
list manipulation algorithms need to be written differently for linked lists whose ends are marked by a sentinel than for those whose ends are marked by some other means.
each list need a place to store the sentinel's identity
a list that is expected to have a sentinel is invalid if it does not have one
There are many ways to implement the details, all with their own advantages and disadvantages.
Personally, I would be inclined (in the non-sentinel case, too) to have a structure to represent an overall list, separate from the structure used to represent a list node. A pointer to the list's head node would be a member of this structure, and in the sentinel-terminated-list case, so would be a pointer to the sentinel node. When you create a new list, you create a sentinel node for it, too; initially, the list's head and sentinel pointers will both point to that node. The head pointer may be changed, but the sentinel pointer must not be. When you append to the list, the appended node gets placed just before the sentinel. It is an error to try to delete the sentinel from the list.
It is to your advantage to write the code for this yourself.
Create it. You said "There should be a pointer to permanently point to the Sentinel Node", so create the pointer. Then use the pointer as the terminator of the list instead of NULL.
Sentinel node - Wikipedia
/*this is a simple LL implementation*/
#include <stdio.h>
#include <stdlib.h>
struct List
{
int data;
struct List *next;
};
struct List sentinel_node_instance;
/* a pointer to permanently point to the Sentinel Node */
struct List* const SENTINEL_NODE = &sentinel_node_instance;
/* the sentinel node should be the first thing on the list when it's created */
struct List* head = SENTINEL_NODE;
void ListInsert(int new_data)
{
struct List *p;
p = (struct List *)malloc(sizeof(struct List));
p->data = new_data;
p->next = (head);
head = p;
}
void printList(void)
{
struct List* q = head;
while (q != SENTINEL_NODE)
{
printf("%d ", q->data);
q = q->next;
}
printf("\n");
}
int main()
{
ListInsert(5);
ListInsert(7);
ListInsert(6);
ListInsert(4);
ListInsert(2);
printList();
return 0;
}
Another variation of a sentinel node is for a circular doubly linked list, where it is both a head node and a sentinel node. Visual Studio implements std::list in this manner.
head.next = pointer to first node or to head if empty list
head.prev = pointer to last node or to head if empty list
first.prev = pointer to head node
last.next = pointer to head node

Freeing a struct

I have a linked list struct on me, and I used a node struct to help me store data and know where each node is pointing to. In the creation of my list, I did this:
struct list {
struct node *Head;
struct node *Tail;
}
struct node{
int val;
struct node * next;
}
struct list create_list(){
struct list new_list;
new_list = malloc(sizeof(struct list));
}
Then, when I am trying to free the list that I malloced for, I did this
void free_list(struct list linked_list){
free(linked_list);
}
However, I realize that I may have malloced wrong, because when you malloc, you need to assign it to a pointer. When I want to free the linked_list that I originally malloced for, nothing really changes.
I think this may be because the linked_list that I sent into free() is not a pointer, thus no changes have been made. I tried adding & and * to it, but I end up getting errors....
Any tips or help is appreciated.
Edit:
Fixed Typo on the struct list.
This is hw, so I cannot change the parameters of the functions, which is what is giving me a hard time freeing the linked list I made. This is because the function free_list() cannot change its parameter into a pointer linked list, so I don't know exactly what to do.
I will also take note of when I am mallocing for the linked list in the create_list function.
Yay for passing the linked list around by value rather than on the heap for something that small.
Is this more like what you want:
struct list create_list(){
struct list new_list = { NULL, NULL };
return list;
}
void free_list(struct list linked_list){
struct node *next;
for (struct node *node = linked_list.Head; node; node = next;)
{
temp = node->next;
free(node);
}
}
Thus freeing only the list nodes that were allocated with malloc().

Nodes as a pointer

Why the nodes in a linked list are declared as pointers? Nodes contains the pointer part in it to link to another node. Then why the nodes are itself a pointer?
struct node
{
int data;
struct node *link;
} *start;
Now we introduce nodes as
struct node *tmp;
Now this is a node which is a pointer to data type struct node..but for linking we use the link pointer to link the other node
Why dindnt we coded node as
struct node tmp;
only...is this because of allocating dynamic memory..or something more?
Yes, this is because the nodes are allocated dynamically.
struct node tmp could be using tmp as a dummy or sentinel node, where it's data is not used, only it's next pointer to the actual first node of a list.
struct node *tmp would be using tmp as a pointer to the first node of a list or as a working pointer to some node in a list when scanning a list.
In some cases a list structure is also used:
struct list{
struct node *head // pointer to first node
struct node *tail // optional: pointer to last node
size_t size; // optional: number of items in a list
}
A circular list could be implemented using just a tail pointer to the last node, which in turn would have a next pointer to the head/first node.

Nodes data type

Why the nodes in a linked list are declared as pointers? Nodes contains the pointer part in it to link to another node. Then why the nodes are itself a pointer?
struct node
{
int data;
struct node *link;
} *start;
Now we introduce nodes as
struct node *tmp;
Now this is a node which is a pointer to data type struct node..but for linking we use the link pointer to link the other node Why dindnt we coded node as
struct node tmp;
only...is this because of allocating dynamic memory..or something more?
Nodes in linked lists in C always allocated in a heap to allow you manage their lifetime. Therefore for creating, destroying and adding nodes to list, you need to declare a pointer on a node:
struct node *tmp; // declaring
tmp = (node*) malloc(sizeof(node)); // creating (allocating memory)
// ... tmp initializing and processing
free(tmp); // removing (freeing memory)
tmp = NULL;
Otherwise, if you create a node on stack node tmp;, it's lifetime will be managed by the rules of language, not by your business logic.

Is it a Linked list implementation?

I am just learning pointers in C and implemented a singly linked list with 3 elements. Is this the right way of approach. Even if it isn't, does the code which I have written represents a linked list?
#include <stdio.h>
struct node
{
int a;
struct node *link;
};
int main()
{
struct node first;
struct node second;
struct node third;
first.a=1;
first.link=&second;
first.link->a=2;
first.link->link=&third;
first.link->link->a=3;
printf("\n%d",first.a);
printf("\n%d",second.a);
printf("\n%d",third.a);
return 0;
}
The code you have written correctly constructs a singly linked list, but is not a general implementation of a singly linked list data structure. For this, the operations that can be applied to a linked list (for example insert an element at a given position, append an element to the end of the list or count the number of elements in the list) are abstracted out and moved into utility functions.
So, as a next step, try implementing a void append(struct node* head, int value) function that, given a pointer to the head of the list, appends a new node with the given value to the end of the list. Then, try to express the construction of your list using this function.
#include <stdio.h>
struct node
{
int a;
struct node *link;
};
int main()
{
struct node first;
struct node second;
struct node third;
first.a=1;
first.link=&second;
first.link->a = 2;
second.link=&third;
second.link->a=3;
printf("\n%d",first.a);
printf("\n%d",second.a);
printf("\n%d",third.a);
return 0;
}
//the nodes in each struct need to point to the next struct, your code was changing the original destination of first.link from pointing to second and then to third,
it cannot point to both, second contains pointer to third, third would contain pointer to fourth....

Resources