In the example below, I created a linked list and I can add numbers successfully. However, at the end of the execution, the function named "traverse" does not work. How can I fix this error?
Here is my code:
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct node
{
int data;
struct node*prev;
struct node*next;
};
void add( node*head,int number )
{
node*ptr = NULL;
if( head == NULL )
{
head = (node*)malloc(sizeof(node));
head->data = number;
head->next = NULL;
head->prev = NULL;
ptr = head;
}
else
{
ptr->next = (node*)malloc(sizeof(node));
ptr->next->prev = ptr;
ptr = ptr->next;
ptr->data = number;
ptr->next = NULL;
}
}
void traverse( node* head )
{
while( head != NULL )
{
printf("%d ",head->data);
head = head->next;
}
}
int main( void )
{
node *head = NULL;
int number;
char response;
printf("%s\n","Do you want to enter a number in linked list(y/n)?" );
scanf("%c",&response);
while( response == 'y' || response == 'Y' )
{
printf("\nEnter num..> ");
scanf("%d",&number);
add(head,number);
printf("%s\n","Do you want to continue(y/n)?" );
response = getche();
}
printf("\nYour doubly linked list\n");
traverse(head);
getch();
return 0;
}
when "traverse" is called, the console print space like the following image.
If you have decided on C, then continuing from the comments, you are attempting to update a local copy of the pointer head
in add()
. As mentioned, you have two option, either change the return type of add()
to node *add()
so you can return ptr
and assign as the new head
back in main()
, or pass the address of head
as the first parameter and update the node stored at the original pointer address in add()
.
You can pass the address of head
to add()
as follows:
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
(note: since you pass the address of head
as a parameter, you must remove one level of indirection from the pointer-to-pointer in add()
by dereferncing head
(e.g. *head
) in order to update the node at the original pointer address. You also need to use the (*head)
when further derferencing the pointer with ->
due to C operator precedence -- so you get the original pointer address before ->
is applied)
Note, the add()
function uses a method call Forward-Chaining to add each node to the list in O(1) time. This also means the list will hold the numbers in the reverse order they were entered (last first). You have two options to insert in-order, (1) iterate to the end of the list each time and add a new end node (highly inefficient for large lists, no longer O(1) time, or (2) use another tail
pointer that always points to the last node to allow in-order insertions in O(1) time.
You would then call your add()
function in main()
with
add (&head, number);
Do NOT make things difficult on yourself when testing your list implementation. There is no reason to have to type 'y'
then a number and 'y'
again before every number you add to your list (that would drive me nuts...). Just add numbers to your list with a loop, you can do input later, e.g.
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
(note: conio.h
has been removed and getchar()
used to hold the terminal open on windows. Since I'm on Linux, the final getchar()
is not compiled as part of my executable)
Your traverse()
function will work, but get in the habit of using a separate separate pointer to iterate over you list. This isn't always required, and isn't needed in traverse()
since you can use the local copy of head
, but always using a temporary pointer to iterate with leave you with the original head
address if you need it for use later in your function, e.g.
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
Notice also the delete_list()
function added to free()
all memory added for your list. You won't always be declaring lists in main()
where the memory is freed on exit. Get in the habit of keeping track of the memory you allocate and freeing the memory before your pointer goes out of scope (otherwise, you will create a memory leak)
The full program would be:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *prev, *next;
} node;
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
void delete_list (node *head)
{
node *iter = head;
while (iter) {
node *victim = iter;
iter = iter->next;
free (victim);
}
}
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
Example Use/Output
$ ./bin/llmess
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/llmess
==16661== Memcheck, a memory error detector
==16661== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16661== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==16661== Command: ./bin/llmess
==16661==
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
==16661==
==16661== HEAP SUMMARY:
==16661== in use at exit: 0 bytes in 0 blocks
==16661== total heap usage: 21 allocs, 21 frees, 1,504 bytes allocated
==16661==
==16661== All heap blocks were freed -- no leaks are possible
==16661==
==16661== For counts of detected and suppressed errors, rerun with: -v
==16661== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.