A linked list is a data structure that
consists of a group of nodes which are linked together. These are
one of the simplest and most common data structures for C programs.
One of the biggest advantages that linked lists have is they are
efficient at inserting and deleting elements within the sequence. The
biggest drawback of linked list is you generally have to traverse the
list to find particular nodes or to get the number of nodes in the
list.
There are several varieties of linked
lists like singly linked, doubly linked and multiply linked. We will
be using singly linked lists in this blog as we create various
networking tools therefore, in this first tutorial, we will build a
singly linked list library that we can integrate with our networking
code. While the code for this library could be optimized and
compacted, I feel that it is more important to make code readable
when learning therefore the code for this library and most of the
code in this blog will be written with functionality and readability
as the primary objectives.
You will be able to see how the linked
list works as we go though the code but first lets take a quick look
at the header file for our linked list library:
#ifndef
LinkedListTest_linkedlistlib_h
#define
LinkedListTest_linkedlistlib_h
struct
node
{
int
num;
char
name[16];
struct
node *next;
};
struct
node *buildnode(int num, char *name);
int
addnode(struct node *head, struct node *add);
int
length(struct node* head);
void
deletelist(struct node **head);
struct
node *searchnode(struct node *head, char *name);
int
removenode(struct node **head, struct node *remove);
#endif
Next we define six functions that we will be implementing for our library. These functions are:
- struct
node *buildnode(int num, char *name)
– This is a convenience method that will build the node given the
values pass
in.
This function and
the searchnode()
function are the two functions
that
we will need to change
when we add this library to our projects.
- int
addnode(struct node *head, struct node *add) –
This function will add a node to the end of our list. The
head node
is the first node in our structure while the add
node
is the node to add. This
function will return the number of elements that are in the list
after the new node is added.
- int
length(struct node *head)
– This function will return the number
of nodes in
the list. The head
node is the first node in the list. If we passed another node,
besides the first node of the list, then
this function
would return the number of nodes from that node to
the end of the list.
- void
deletelist(struct node **head)
– Deletes the list and frees the memory being used.
- struct
node *searchnode(struct node *head, char *name) –
This function will search the list and return the first structure
that
has a name
element equal to the
name
argument.
- int
removenode(struct node **head, struct node *remove)
– This function will search the list for the remove
node and remove it from the list. This function will return a 0 if
it was successful in finding
and removing
the node or a -1 if the remove
node could not be found.
struct
node *buildnode(int num, char *name) {
struct
node *new = (struct node *)malloc(sizeof(struct node));
new->num
= num;
strncpy(new->name,
name, sizeof(new->name));
new->next
= NULL;
return
new;
}
This
function starts off by allocating memory needed
for
the
new node
structure with the malloc()
function. We will
need to manually release this
memory
when we are finished
with this node
otherwise we will have memory leaks in our application.Next we assign the values to our structure. Notice that we use the strncpy() function to copy the name value into our structure. This is done because the char pointer that was passed to our function may get released or reused, so in order to make sure that we do not lose the value we copy it into the structure. We also set the next pointer to NULL because it is not currently part of the linked list. Remember this function just creates a node but does not put it in a linked list. To put a node into a linked list we we use the addnode() function.
Now we will look at our int addnode(struct node *head, struct node *add) function. This function will add a node to the end of the list and return the total number of nodes after the node is added.
int
addnode(struct node *head, struct node *add) {
struct
node* current = head;
int
count = 0;
while
(current->next != NULL) {
count++;
current
= current->next;
}
current->next
= add;
count++;
return
count;
}
The
addnode()
function
accepts two arguments; the first argument is the first node of
the list and the second argument is the node to add. In order to add
a node to the end of the list, we need to loop though the list to
find the end. This
is a weakness of singly linked list; since
we do not store the end of the list or the number of nodes in the
list we need to loop though the whole list to determine the number of
nodes or to find the end of the list.Since we do not want to move the pointer that was passed into the function which points to the first node of the list, we create a new pointer that we name current that also points to the first node of the list. We then set up a while loop that loops through the list until we reach the end of the list. We know that we have found the last node when the next pointer of that node points to NULL rather than another node. If the next pointer does not point to NULL, we increment our counter and move the current point to point to the next node in our list.
Once we find the last node we set the next pointer of that node to point to the node that we are adding to the list. This will put the new node at the end of the list. This does assume that the next pointer within the node that we are adding points to NULL and not another node in the list. If the next pointer in the new node points to another node in the list then we would set up a linked list that loops around in a circle. This would create an endless loop when we attempt to find the end of the list or when we search for a specific node in the list. To be on the safe side we could put a line in the function that sets the next pointer of the new node to NULL but I left that out because this function could also be used to combine two linked lists where the next pointer may point to another node in a separate list.
Next we will look at the int length(struct node *head) function. This function will return the number of nodes in our list. This function accepts one argument which is the first node in the list. Here is the code for the length() function:
int
length(struct node *head) {
struct
node* current = head;
int
count = 0;
while
(current != NULL) {
count++;
current
= current->next;
}
return
count;
}
The
length function is a duplicate of our addnode()
function
described earlier except that we are not adding a new node to the
list once
we find the end of the list.Now lets look at the void deletelist(struct node **head) function. This function will delete the whole list and free the memory that the list uses. This function takes one argument which is a pointer to a pointer that points the the first node of the list to remove. Here is the code for our deletelist() function.
void
deletelist(struct node **head) {
struct
node *next;
struct
node *current = *head;
while
(current) {
next
= current->next;
free(current);
current=next;
}
*head
= NULL;
}
In
the deletelist()
function we loop through the list. For each node in
the list we
assign the next
pointer to point to the next node in the list. We
then use the free()
function to free the memory that is assigned to the current node.
This
allows your application to reuse this memory location and prevents
memory leaks. We then set the current
pointer to point to the next node in the list. Finally, once all of
the nodes are removed, we set the pointer which pointed to the first
node in the list to NULL
to prevent our application from using it by mistake.Now lets see how the struct node *searchnode(struct node *head, char *name) function works. This functions searches the linked list for the first node that has a name value that is equal to the name argument passed to the function. This function takes two arguments; the first argument is a pointer to the first node in the linked list and the second argument is a pointer to the char string that we are searching for. Lets look at the code for the searchnode() function.
struct
node *searchnode(struct node *head, char *name) {
struct
node* current = head;
while
(current != NULL) {
if
(strcmp(current->name,name) == 0)
return
current;
current
= current->next;
}
return
NULL;
}
We
start off by creating a while loop that loops though our list.
We
then compare the name
element of each node with the name
argument passed into the function using the strcmp()
function. If they are equal, then
we found the node we are looking for and
return the
current node. If they are not equal, we our
loop moves
to the next node. If we are unable to find a node that contains the
name
argument we return NULL.Finally lets look at the int removenode(struct node **head, struct node *remove) function. This function will remove a specified node from our linked list. This function takes two arguments; the first argument being a pointer to a pointer that points to the linked list to search and the second argument being a pointer to the node to remove. The removenode() function will return 0 if the node was found and removed or -1 if the node was not removed. Lets look at the code:
int
removenode(struct node **head, struct node *remove) {
struct
node* current = *head;
struct
node* prev = NULL;
while
(current != NULL) {
if
(current == remove) {
if
(prev == NULL)
*head
= current->next;
else
prev->next
= current->next;
free(current);
return
0;
}
prev=current;
current
= current->next;
}
return
-1;
}
We begin the removenode()
function
by defining two pointers. We set the first pointer, current,
to point to the first node of our list and set the second pointer,
prev,
to point to NULL.
When we set up our while loop that will loop though our list, the
current
pointer will always point to the current node that we are checking
and the prev
pointer will always point to the previous node. We will need to know
the previous node in our list because when we find the node to
remove, well will need to change the next
pointer of the previous node so it no longer points to the node we
wish to remove. Lets look at a diagram that shows how we are going
to remove a node from our list.In the diagram we can see that in order to remove node2 from our list we need to change the next pointer of node1 to point to node3 rather than node2. We would then free the memory allocated to node2 so we do not have memory leaks in our application.
Once we have our two pointers defined we create a while loop that will loop though our list.
If the current node is equal to the remove node then this is the node to remove. If this is the node to remove we check to see if it is the first node in the list by seeing if the prev pointer is pointing to NULL. If this is the first node then we change the head pointer, that was passed into our function, to point to the next node in the list otherwise we set the next pointer from the previous structure to point to the node that the current node's next pointer is pointing too. Finally we free the memory that is used by the current node and return 0 signifying that we found and removed the node.
If the current node is not equal to the remove node then we set the prev pointer to point to the current node and the current pointer to point to the next node in the list. If we loop though the whole list and we do not find the remove node, then we return a -1 signifying that we could not find the node to remove.
Now lets look at some code to test our linked list library:
#include
<stdio.h>
#include
"linkedlistlib.h"
#include
<time.h>
void
waitFor(unsigned int secs) {
long
retTime = time(0) + secs;
while
(time(0) < retTime);
}
int
main()
{
printf("Hello
world\n");
int
i;
for
(i=0; i< 500; i++) {
struct
node *first = buildnode(1,"Jon");
struct
node *second = buildnode(2, "Kim");
struct
node *three = buildnode(3, "Kailey");
struct
node *four = buildnode(4, "Kara");
addnode(first, second);
addnode(first,
three);
addnode(first,
four);
printf("%d)----------\n",i);
printf("Count
%d\n", length(first));
struct
node *found = searchnode(first, "Kailey");
if
(found != NULL)
printf("found:
%d %s\n",found->num, found->name);
removenode(&first,
first);
printf("Count
after remove %d\n", length(first));
removenode(&first,
found);
printf("Count
after second remove %d\n", length(first));
deletelist(&first);
printf("found:
%d\n",length(first));
printf("-------------------\n");
waitFor(2);
}
return
(0);
}
Inside the main() function, we begin by creating a for loop that will loop 500 times. This loop will allow us to see how our library performs by repeating the various tasks 500 times. If we analyze the memory usage while this runs it will also help us to detect any memory leaks.
Within the for loop we create four nodes using the buildnode() function. We then create our linked list by adding the last three nodes to our list using the addnode() function. Notice that we do not add the first node to the list with the addnode() function, that is because we use that first node as the first node for the list.
Now that we have our list built we verify that the list contains four nodes by using the length() function which returns the number of nodes in our list and print that count to the screen.
Next we use the searchnode() function to search for a node that contains the name “Kailey”. If we find a node that contains “Kailey” then we print the num and name elements of that structure to the screen.
We then try to remove the first node in our list by calling the removenode() function. We verify that the node was removed by printing out the total number of nodes in the list after we called the removenode() function. To verify that we can correctly remove multiple nodes, we then remove the “Kailey” node that we found earlier in this loop and once again print out the number of nodes in the list to the screen.
Finally we delete the list using the deletelist() function and once again print out the number of nodes in the list to verify that they were all removed.
I hope this post helps you understand linked lists a little better.
No comments:
Post a Comment