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.
Related
I'm trying to create a code where there are the names (e.g. Andy, Barry, Matilda) and the names will be removed when I enter a certain substring (e.g. substring is y, therefore Andy and Barry will be removed. The only one left is Matilda). Can anyone offer me assistance?
My code is:
void del(char key[])
{
if(head == NULL)
{
printf("There's no data\n");
}
else{
curr = head;
while(curr != NULL && strcmp(curr->name, key) != 0)
{
curr = curr->next;
}
if(curr == NULL)
{
printf("Node is not in the list\n");
}
if(curr == head & curr == tail)
{
free(curr);
head = tail = NULL;
}
else if(curr == head)
{
head = head->next;
free(curr);
head->prev = NULL;
}
else if(curr == tail)
{
tail = tail->prev;
free(curr);
tail->next = NULL;
}
else
{
curr->prev->next = curr->next;
curr->next->prev = curr->prev;
free(curr);
}
}
}
Your code correctly removes a single list node, except when key is not found - in this case, a return after printf("Node is not in the list\n"); is missing.
To remove multiple nodes, we have to loop over the list, continuing also if a match is found and deleted. To print the message Node is not in the list only if nothing was found, we can use a flag which is tested after the loop. So, you can replace the content of your outer else block e. g. with
void *next;
int deleted = 0;
for (curr = head; curr; curr = next)
{ next = curr->next;
if (strstr(curr->name, key))
{ deleted = 1;
// the rest of this block is your code unchanged
if(curr == head & curr == tail)
{
free(curr);
head = tail = NULL;
}
else if(curr == head)
{
head = head->next;
free(curr);
head->prev = NULL;
}
else if(curr == tail)
{
tail = tail->prev;
free(curr);
tail->next = NULL;
}
else
{
curr->prev->next = curr->next;
curr->next->prev = curr->prev;
free(curr);
}
}
}
if (!deleted) printf("Node is not in the list\n");
You have not offered a lot of information to go on, but I take that is being new to the community. From what you do provide, it is clear that, at minimum, you have a Doubly-Linked-List where you have a string as the data member. It is also clear that you have declared both the head and tail pointer as global variables (not a great practice, as you should pass any information required by your function as a parameter, but for this learning exercise it provides minimal simplification)
From what you describe you want your del function to do, you want to iterate over your linked-list testing whether the name member contains the substring key and if so, you want to delete that node from your list.
You have two primary problems:
you use the wrong string function to check for a substring in your name member. strcmp() will only find the nodes where the complete name matches your key. Instead you want strstr() where you can match key anywhere within name;
you over-complicate the node deletion by attempting to use a pointer to the current node alone to iterate over the list, instead of using both a pointer and the address for the current node. See Linus on Understanding Pointers
To correct your fist problem, you simply need to change strcmp() to strstr() fot match key anywhere in name, e.g.
...
if (strstr(curr->name, key) != NULL) { /* strstr, not strcmp, to find key */
...
To solve the second problem, you need to rework your del function. To do so, you have to understand how iterating while removing multiple-nodes differs from iterating to find a single-node to delete. When looping to find a single-node to delete, you simply advance to the next node in your list on each iteration until the node is found and then you delete that node.
You cannot do that when potentially deleting multiple nodes from your list. Why? When you delete a node with a matching substring, the next node in the list takes its place. You can't just advance to the next node after this deletion because the node that is now the current node after you delete the first may also contain the substring you want to find. If you just blindly advance to the next node and the current after deletion also contains the substring, you will skip over deleting that node.
What this means from a code standpoint is you need to add an else clause below your if, e.g.
if (strstr(curr->name, key) != NULL) { /* strstr, not strcmp, to find key */
...
else
...
Under your if clause, the next node replaces the current after deletion, so you do NOT advance again to the next node in your list. That way the new current node will be checked for the matching substring on the next iteration. You only advance to the next node under your else clause if key does not match.
When iterating with both a pointer to the current node along with the address of the current node, you do not have to handle special cases. You will always set the content at the current address to the next node in your list. (head doesn't change, because you have set the struct at that address to the next on deletion) The only check you need is to check if you are deleting the tail node. In that case you need to update the tail node to point to the previous node as you will be deleting what tail currently points to. Otherwise, all you need to do is update the ->prev pointer of the node you moved into the current address to point to the previous node in the list.
With that in mind, your del function reduces to:
void del (char key[])
{
if (head == NULL) { /* check empty */
puts ("list-empty");
return;
}
node_t **ppnode = &head, /* address of current node */
*curr = head; /* pointer to current node */
while (curr) {
if (strstr(curr->name, key) != NULL) { /* strstr, not strcmp, to find key */
*ppnode = curr->next; /* fill address w/next node */
if (curr != tail) /* if not tail */
(*ppnode)->prev = curr->prev; /* set ->prev to prev node */
else /* otherwise */
tail = curr->prev; /* update tail pointer */
free (curr); /* free node */
curr = *ppnode; /* set new current node */
}
else { /* node to keep */
ppnode = &curr->next; /* set address to addres of next */
curr = curr->next; /* advance to next node */
}
}
}
Without knowing more about your code, all we can do is write a short example that adds your strings as nodes in a list (using a fixed name to simplify things). A short example like:
int main (void) {
add ("Andy"); /* add nodes to list */
add ("Barry");
add ("Matilda");
prnfwd(); /* print forward and reverse */
prnrev();
putchar ('\n');
del ("y"); /* delete nodes containing substring "y" */
prnfwd(); /* print forward and reverse */
prnrev();
del_list(); /* free allocated memory */
}
That adds the nodes, iterates over the list in both directions, calls del ("y"); to delete all nodes where the string contains the substring "y" (note that is different from the character 'y'), and then iterates over the list again in both direction outputting what remains before freeing all memory in the list.
Example Use/Output
The result given your example strings would be:
$ ./bin/lldglobaldelkey
Andy Barry Matilda
Matilda Barry Andy
Matilda
Matilda
The full implementation of the example is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXNM 64
typedef struct node_t {
char name[MAXNM];
struct node_t *prev, *next;
} node_t;
node_t *head, *tail;
/** add node at end of list, update tail to end */
node_t *add (const char *s)
{
node_t *node = malloc (sizeof *node); /* allocate node */
if (!node) /* validate allocation */
return NULL;
strcpy (node->name, s); /* initialize new node */
node->prev = node->next = NULL;
if (!head) /* if 1st node, node is head/tail */
head = tail = node;
else { /* otherwise */
node->prev = tail; /* set prev to tail */
tail->next = node; /* add at end, update tail pointer */
tail = node;
}
return node; /* return new node */
}
/* print list forward */
void prnfwd (void)
{
if (!head) { /* check empty */
puts ("list-empty");
return;
}
for (node_t *n = head; n; n = n->next) /* iterate over nodes - forward */
printf (" %s", n->name);
putchar ('\n');
}
/* print list reverse */
void prnrev (void)
{
if (!head) { /* check empty */
puts ("list-empty");
return;
}
for (node_t *n = tail; n; n = n->prev) /* iterate over nodes - reverse */
printf (" %s", n->name);
putchar ('\n');
}
/** delete all nodes in list */
void del_list (void)
{
node_t *n = head;
if (!head) { /* check empty */
puts ("list-empty");
return;
}
while (n) { /* iterate over nodes - forward */
node_t *victim = n; /* save ptr to node to delete */
n = n->next; /* advance to next */
free (victim); /* delete node */
}
head = tail = NULL; /* set pointers NULL */
}
void del (char key[])
{
if (head == NULL) { /* check empty */
puts ("list-empty");
return;
}
node_t **ppnode = &head, /* address of current node */
*curr = head; /* pointer to current node */
while (curr) {
if (strstr(curr->name, key) != NULL) { /* strstr, not strcmp, to find key */
*ppnode = curr->next; /* fill address w/next node */
if (curr != tail) /* if not tail */
(*ppnode)->prev = curr->prev; /* set ->prev to prev node */
else /* otherwise */
tail = curr->prev; /* update tail pointer */
free (curr); /* free node */
curr = *ppnode; /* set new current node */
}
else { /* node to keep */
ppnode = &curr->next; /* set address to addres of next */
curr = curr->next; /* advance to next node */
}
}
}
int main (void) {
add ("Andy"); /* add nodes to list */
add ("Barry");
add ("Matilda");
prnfwd(); /* print forward and reverse */
prnrev();
putchar ('\n');
del ("y"); /* delete nodes containing substring "y" */
prnfwd(); /* print forward and reverse */
prnrev();
del_list(); /* free allocated memory */
}
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/lldglobaldelkey
==10704== Memcheck, a memory error detector
==10704== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10704== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10704== Command: ./bin/lldglobaldelkey
==10704==
Andy Barry Matilda
Matilda Barry Andy
Matilda
Matilda
==10704==
==10704== HEAP SUMMARY:
==10704== in use at exit: 0 bytes in 0 blocks
==10704== total heap usage: 4 allocs, 4 frees, 1,264 bytes allocated
==10704==
==10704== All heap blocks were freed -- no leaks are possible
==10704==
==10704== For counts of detected and suppressed errors, rerun with: -v
==10704== 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.
I hope this is close to what your implementation is. Look things over and let me know if you have further questions.
The nodes get inserted when I insert them in order on the console
How do I make sure that I take care of all boundary conditions? For example, what if the user enters a position greater than the size of the list? Also, I get a segmentation fault when I try to insert after a node but it works just fine before a node. Here's a picture to help explain my problem better
Also, I get a segmentation fault when I try to insert after a node but it works just fine before a node.
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *head = NULL;
struct Node *insert(int x,int pos)
{
if(head == NULL)
{
struct Node *temp = (struct Node*)malloc(sizeof(struct Node));
temp->data = x;
temp->next = head;
head = temp;
return head;
}
else
{
int len = 0;
struct Node *temp = head;
while(temp!=NULL)
{
++len;
temp = temp->next;
}
if(pos == 1)
{
struct Node *temp = (struct Node*)malloc(sizeof(struct Node));
temp->data = x;
temp->next = head;
head = temp;
return head;
}
else
{
struct Node *temp = (struct Node*)malloc(sizeof(struct Node));
temp->data = x;
temp->next = NULL;
struct Node *temp1 = head;
for(int i = 2; i<pos; i++)
{
temp1 = temp1->next;
}
temp->next = temp1->next;
temp1->next= temp;
}
}
}
void print()
{
struct Node *temp = head;
while(temp!=NULL)
{
printf("%d ",temp->data);
temp = temp->next;
}
printf("\n");
}
int main()
{
int n,i,x,pos;
printf("How many elements?\n");
scanf("%d",&n);
for(i = 0; i<n; i++)
{
printf("enter the value and the position: \n");
scanf("%d %d",&x,&pos);
insert(x,pos);
print();
}
printf("Linked list is: \n");
print();
}
Output 1
How many elements?
3
enter the value and the position:
3 2
List is: 3
enter the value and the position:
4 3
Segmentation fault (core dumped)
Output 2
How many elements?
3
enter the value and the position:
3 2
List is: 3
enter the value and the position:
4 1
List is: 4 3
enter the value and the position:
5 3
List is: 4 3 5
Linked list is:
4 3 5
You are making your insert much harder than it needs to be. You simply need to iterate with 2 conditions (1) pos times or less, and (2) while the next pointer is not NULL. You can greatly simplify the insert by iterating using both the address of and a pointer to the next node. You maintain the current node using the address and your pointer always points to the next node. When you have iterated pos times or your next pointer is NULL you insert your node. See Linus on Understand Pointers
Further, you are not making use of any return, from insert(), so your function prototype should simply be void insert (int x, int pos). While you should avoid using a global pointer to your list, for your purposes of this limited example, that is fine. Know that your list should normally be declared in the scope it is needed and a pointer (or pointer to pointer) to the beginning of the list should be passed as a parameter to make the list available to any function that operates on it rather than it being global.
Putting the pieces together, your insert() function reduces to:
void insert (int x, int pos)
{
struct Node **ppn = &head, /* pointer to pointer to node */
*pn = head, /* pointer to node */
*node = malloc (sizeof *node); /* allocate for new node */
if (!node) { /* validate allocation */
perror ("malloc-node");
exit (EXIT_FAILURE);
}
node->data = x; /* initialize members values */
node->next = NULL;
while (pos-- && pn) { /* iterate pos times && while pn != NULL */
ppn = &pn->next;
pn = pn->next;
}
node->next = pn; /* set next to pointer to node */
*ppn = node; /* set node at address to node */
}
Adding that to the remainder of your example, your complete example would be:
#include<stdio.h>
#include<stdlib.h>
struct Node {
int data;
struct Node *next;
};
struct Node *head = NULL;
void insert (int x, int pos)
{
struct Node **ppn = &head, /* pointer to pointer to node */
*pn = head, /* pointer to node */
*node = malloc (sizeof *node); /* allocate for new node */
if (!node) { /* validate allocation */
perror ("malloc-node");
exit (EXIT_FAILURE);
}
node->data = x; /* initialize members values */
node->next = NULL;
while (pos-- && pn) { /* iterate pos times && while pn != NULL */
ppn = &pn->next;
pn = pn->next;
}
node->next = pn; /* set next to pointer to node */
*ppn = node; /* set node at address to node */
}
/** print all nodes in list */
void print (void)
{
if (!head) {
puts ("list-empty");
return;
}
for (struct Node *n = head; n; n = n->next)
printf (" %d", n->data);
putchar ('\n');
}
/** delete all nodes in list */
void del_list (void)
{
struct Node *n = head;
while (n) {
struct Node *victim = n;
n = n->next;
free (victim);
}
}
int main()
{
int n,i,x,pos;
printf ("How many elements?\n");
if (scanf ("%d",&n) != 1) {
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
for (i = 0; i < n; i++)
{
printf ("enter the value and the position: \n");
if(scanf("%d %d",&x, &pos) == 2) {
insert (x, pos);
fputs ("list is: ", stdout);
print();
}
}
puts ("\nLinked list is:");
print();
del_list(); /* free all memory allocated to list */
}
(note: a del_list() function was added to free() the memory associated with your list.)
Example Use/Output
Continuing with your example where you were SegFaulting, you can now complete your inserts, e.g.:
$ ./bin/llatpos
How many elements?
3
enter the value and the position:
3 2
list is: 3
enter the value and the position:
4 3
list is: 3 4
enter the value and the position:
1 0
list is: 1 3 4
Linked list is:
1 3 4
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/llatpos
==16615== Memcheck, a memory error detector
==16615== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16615== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==16615== Command: ./bin/llatpos
==16615==
How many elements?
3
enter the value and the position:
3 2
list is: 3
enter the value and the position:
4 3
list is: 3 4
enter the value and the position:
1 0
list is: 1 3 4
Linked list is:
1 3 4
==16615==
==16615== HEAP SUMMARY:
==16615== in use at exit: 0 bytes in 0 blocks
==16615== total heap usage: 5 allocs, 5 frees, 2,096 bytes allocated
==16615==
==16615== All heap blocks were freed -- no leaks are possible
==16615==
==16615== For counts of detected and suppressed errors, rerun with: -v
==16615== 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.
For starters indices in C (for example for arrays) start from 0.
So you should also use the value pf the position in the list that starts from 0.
Logically if the given position is greater than or equal to the number of nodes in the list the new value should be appended to the list.
Nowhere in the function insert you are using the size of the list. So this code snippet
int len = 0;
struct Node *temp = head;
while(temp!=NULL)
{
++len;
temp = temp->next;
}
does not make sense.
If the position is greater than the size of the list then this code snippet
for(int i = 2; i<pos; i++)
{
temp1 = temp1->next;
}
can invoke undefined behavior because inside the loop the variable temp1 can be set to NULL.
Also if the position equal to 2 then the node head is not updated.
The function has a non-void return type but returns nothing.
The function insert can be written much simpler as it is shown in the demonstrative program.
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *head = NULL;
int insert( int data, size_t pos )
{
struct Node *temp = malloc( sizeof( struct Node ) );
int success = temp != NULL;
if ( success )
{
temp->data = data;
struct Node **current = &head;
while ( pos-- != 0 && *current != NULL )
{
current = &( *current )->next;
}
temp->next = *current;
*current = temp;
}
return success;
}
void print()
{
for ( const struct Node *current = head; current != NULL; current = current->next )
{
printf( "%d -> ", current->data );
}
puts( "NULL" );
}
int main(void)
{
/*
insert( 3, 2 );
printf( "Linked list is: " );
print();
insert( 4, 3 );
printf( "Linked list is: " );
print();
*/
insert( 3, 3 );
printf( "Linked list is: " );
print();
insert( 1, 0 );
printf( "Linked list is: " );
print();
insert( 2, 1 );
printf( "Linked list is: " );
print();
insert( 4, 3 );
printf( "Linked list is: " );
print();
insert( 5, 10 );
printf( "Linked list is: " );
print();
return 0;
}
The program output is
Linked list is: 3 -> NULL
Linked list is: 1 -> 3 -> NULL
Linked list is: 1 -> 2 -> 3 -> NULL
Linked list is: 1 -> 2 -> 3 -> 4 -> NULL
Linked list is: 1 -> 2 -> 3 -> 4 -> 5 -> NULL
i'm having trouble with building a linked list using two structs
node - contains data and pointer to the next, and list which contains pointer to the head of the list.
i managed to implement it with only the node struct.
i have initialized a struct of a list in the main function
than allocated memory for a list struct using malloc
than i allocated memory for the head which is a pointer to the first node
sent it to another function where there the input,allocating,assigning goes,
but im having hard time to understand how to go through the list without changing the pointer to the head.
and after im done with the nodes and assignment how to get the head pointer to
point back at the start of the list.
should i work with copies? (node *temp) ??
thanks everyone!
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct node
{
int data;
struct node *next;
}node;
typedef struct list
{
struct node *head;
}list;
void main()
{
list *list_a;
list_a = (list*)malloc(sizeof(list));
list_a->head = (node*)malloc(sizeof(node));
assignment(list_a);
}
void assignment(list *list_a)
{
int data;
printf("please enter the numbers:\n(to stop enter ""-1"")\n\n");
scanf("%d", &data);
while (data != -1)
{
list_a->head->data = data;
list_a->head->next = (node*)malloc(sizeof(node));
list_a->head = list_a->head->next;
printf("enter a number:\n");
scanf("%d", &data);
}
}
There are a number of ways to do a linked list, from the mind-numbingly simple add-at-head (which ends up with a list in reverse order) to a fairly standard add-at-tail where you iterate over your nodes to find the end node, and add the new node there. In all cases, it is just a matter of handling your pointers correctly, allocating storage (for both your list parent struct, and each node) and validating all allocations, and then cleaning up after yourself and freeing the used memory when it is no longer needed.
Nested structures where you have a struct holding the head node (and hopefully other useful data to justify the nested approach) are quite common, but there is no need for a parent struct for the list itself. The list address is simply the address of the first node.
When learning lists, it really helps to break the tasks of managing the list down into simple separate functions. This allows you to concentrate (a bit easier) on each list operation singularly. For example, with your list, you will need to:
create/allocate for each node, initialize next pointer NULL and set the data value;
create/allocate for your list, allocating for head and initializing any additional information contained in the list struct;
add nodes to your list, creating the list if needed and adding the node setting data to the proper value and updating any list information needed;
get data out of your list; and
finally freeing the memory for your nodes and your list when it is no longer needed.
In your case, and continuing from my comment to your question, you can declare your structure and typedefs similar to the following:
typedef struct node {
int data;
struct node *next;
} node_t;
typedef struct list {
struct node *head;
size_t n; /* at least make parent hold list size */
} list_t;
Here we a simply added a counter to track the number of nodes in your list as an additional, useful, piece of data to justify the outer stuct. It gives you the node count without having to iterate over the list each time to obtain in (it's just a small efficiency improvement if you need that data). You have the number of nodes in your list with a simple list->n.
Following our list outline, you need a way to create nodes for your list. Whether it is the first node, or last node, you don't care. When you need a node, your create_node function should handle the allocation/validation and initialization. Nothing fancy is needed, e.g.
/** function to create node and set data value to data.
* returns new node on success, NULL otherwise.
*/
node_t *create_node (int data)
{
node_t *newnode = malloc (sizeof *newnode); /* allocate */
if (newnode == NULL) { /* validate/handle error */
perror ("create_node() malloc-newnode");
return NULL;
}
newnode->data = data; /* initialize members */
newnode->next = NULL;
return newnode; /* return pointer to new node */
}
Your create_list function simply needs to allocate for the list struct (and I also have it allocate the first node and initialize the values and pointer 0/NULL). You can have it do whatever you like, e.g. add another parameter passing data for the fist node, etc. I simply have it create the list and first node.
/** function to create list and allocates/initilizes head, set list->n = 0.
* returns new list on success, NULL otherwise.
*/
list_t *create_list (void)
{
node_t *head = NULL;
list_t *list = malloc (sizeof *list); /* allocate list */
if (!list) { /* validate/handle error */
perror ("create_list() malloc-list");
return NULL;
}
head = create_node (0); /* create the first node */
if (!head) /* validate/handle error */
return NULL;
list->head = head; /* initialize list values */
list->n = 0;
return list; /* return list */
}
Your add_node function can be fairly simple, but for purposes here, rather than just stopping if the list is not yet allocated, we will have the add_node function create the list if it doesn't exists and then add the node. This choice has important implications. Since I will handle the case where the list doesn't exist, that means the list address may change within the function. To handle this potential, I must pass the address-of the list as a parameter (e.g. list_t **list instead of simply list_t *list). By having the actual address of the pointer, I can change where the original pointer points and that change will be visible back in the calling function (rather changing where a copy of the pointer points which would not be seen back in the caller).
The function needs to handle two cases (1) "am I the first node?" and (2) "if I'm not the first node, then iterate to end and add there". You can do something similar to:
/** add node to list, create list if list NULL, set node->data to data.
* return new node on success, NULL otherwise.
*/
node_t *add_node (list_t **list, int data)
{
node_t *node;
if (!*list) { /* handle list doesn't exist */
*list = create_list();
if (!*list)
return NULL;
node = (*list)->head; /* (..)-> required by operator precedence */
node->data = data;
}
else { /* list already exists */
node = (*list)->head; /* set node to list->head */
/* iterate over nodes to find last and add node at end */
while (node->next)
node = node->next;
node->next = create_node (data); /* allocate next node */
node = node->next; /* change to new node */
}
(*list)->n++; /* increment number of nodes in list */
return node; /* return node */
}
By doing it this way, I can simply declare the pointer in main() and initialize it NULL and then simply call add_node(&list, x) in main() letting the list functions handle the pointers and allocation.
Your additional list functions are just functions that iterate over each node in the list doing something with the information like printing the list or freeing all the nodes in the list. (pay careful attention to how the node-to-be-freed (e.g. the victim) is handled in the free_list function)
/** print the value of each node in list */
void prn_list (const list_t *list)
{
/* iterate over list printing data value */
for (node_t *node = list->head; node; node = node->next)
printf (" %d", node->data);
putchar ('\n'); /* tidy up with newline */
}
/** free all nodes in list and free list */
void free_list (list_t *list)
{
node_t *node = list->head; /* set node to head */
while (node) { /* iterate over each nod */
node_t *victim = node; /* setting victim to free */
node = node->next; /* change to next node */
free (victim); /* free victim */
}
free (list); /* free list */
}
(note the two different examples of iterating over the nodes using either a for or while loop)
Putting all the pieces together, adding 25 nodes, and printing them out before freeing all memory associated with the list, you could do something like the following:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#if ! defined (_WIN32) && ! defined (_WIN64)
#include <stdlib.h> /* Linux has malloc/free in the stdlib header */
#endif
typedef struct node {
int data;
struct node *next;
} node_t;
typedef struct list {
struct node *head;
size_t n; /* at least make parent hold list size */
} list_t;
/** function to create node and set data value to data.
* returns new node on success, NULL otherwise.
*/
node_t *create_node (int data)
{
node_t *newnode = malloc (sizeof *newnode); /* allocate */
if (newnode == NULL) { /* validate/handle error */
perror ("create_node() malloc-newnode");
return NULL;
}
newnode->data = data; /* initialize members */
newnode->next = NULL;
return newnode; /* return pointer to new node */
}
/** function to create list and allocates/initilizes head, set list->n = 0.
* returns new list on success, NULL otherwise.
*/
list_t *create_list (void)
{
node_t *head = NULL;
list_t *list = malloc (sizeof *list); /* allocate list */
if (!list) { /* validate/handle error */
perror ("create_list() malloc-list");
return NULL;
}
head = create_node (0); /* create the first node */
if (!head) /* validate/handle error */
return NULL;
list->head = head; /* initialize list values */
list->n = 0;
return list; /* return list */
}
/** add node to list, create list if list NULL, set node->data to data.
* return new node on success, NULL otherwise.
*/
node_t *add_node (list_t **list, int data)
{
node_t *node;
if (!*list) { /* handle list doesn't exist */
*list = create_list();
if (!*list)
return NULL;
node = (*list)->head; /* (..)-> required by operator precedence */
node->data = data;
}
else { /* list already exists */
node = (*list)->head; /* set node to list->head */
/* iterate over nodes to find last and add node at end */
while (node->next)
node = node->next;
node->next = create_node (data); /* allocate next node */
node = node->next; /* change to new node */
}
(*list)->n++; /* increment number of nodes in list */
return node; /* return node */
}
/** print the value of each node in list */
void prn_list (const list_t *list)
{
/* iterate over list printing data value */
for (node_t *node = list->head; node; node = node->next)
printf (" %d", node->data);
putchar ('\n'); /* tidy up with newline */
}
/** free all nodes in list and free list */
void free_list (list_t *list)
{
node_t *node = list->head; /* set node to head */
while (node) { /* iterate over each nod */
node_t *victim = node; /* setting victim to free */
node = node->next; /* change to next node */
free (victim); /* free victim */
}
free (list); /* free list */
}
int main (void)
{
list_t *list = NULL; /* just declare list and set pointer NULL */
for (int i = 0; i < 25; i++) /* add 25 nodes to list */
if (add_node (&list, i + 1) == NULL) /* validate each addition */
break;
/* print list content, beginning with number of nodes in list */
printf ("list contains: %lu nodes\n\n", list->n);
prn_list (list); /* followed by each node value */
free_list (list); /* and then delete list */
return 0;
}
Example Use/Output
$ /bin/llsingle_w_parent
list contains: 25 nodes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
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 insure 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/llsingle_w_parent
==14749== Memcheck, a memory error detector
==14749== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14749== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==14749== Command: ./bin/llsingle_w_parent
==14749==
list contains: 25 nodes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
==14749==
==14749== HEAP SUMMARY:
==14749== in use at exit: 0 bytes in 0 blocks
==14749== total heap usage: 26 allocs, 26 frees, 416 bytes allocated
==14749==
==14749== All heap blocks were freed -- no leaks are possible
==14749==
==14749== For counts of detected and suppressed errors, rerun with: -v
==14749== 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.
Linked-lists come in all different implementation. You should be aware of a couple of basic distinctions. You have lists that use dummy-node (e.g. dummy head and tail node that do not hold data, but just point to the first/last node in the list). You have circular-linked-lists where the last node points back to the first node (this allows iterating from any node in the list, to any node in the list in a circular fashion across the start and end). So when you look at "linked-list" code, understand that there can be many ways that lists are implemented, all have strengths and weaknesses, so you just have to match your list for the job.
Finally, as specified in the comment, your declaration void main() is incorrect and is an ancient throwback to the early days of windows (like DOS 3.3 and Windows 3.1, Trumpet WinSock, and the Borland Turbo C compiler days) The proper declarations for main are int main (void) and int main (int argc, char **argv) (which you will see written with the equivalent char *argv[]). note: main is a function of type int and it returns a value. See: C11 Standard ยง5.1.2.2.1 Program startup p1 (draft n1570). See also: See What should main() return in C and C++?
(note: there are some embedded systems that continue to use void main(), but those are the exceptions, not the rule, and are non-compliant with the C-Standard)
Look things over and let me know if you have further questions.
but im having hard time to understand how to go through the list without changing the pointer to the head
Head itself would be the pointer to the first node.
and after im done with the nodes and assignment how to get the head pointer to point back at the start of the list.
You make the new node point to the first node & then shift the pointer pointing to the first node i.e. head to point to the newly added node.
IMPORTANT - You are missing stdlib.h without which malloc can't be used.
Here is a crude version (just for understanding):
while(/*IF YOU WANT TO ADD NODES?*/)
{
if(head == NULL)
{
head = malloc((sizeof(struct node));
head->data = //USER INPUT;
head->next=NULL;
}
else
{
temp = malloc(sizeof(struct node));
temp->data = //USER INPUT;
temp->next = head;
head=temp;
}
}
This entire this can be seen in several steps:
First: head->[data||NULL]
Second: temp->[data||Pointer pointing to 1st node]->(head)[data||NULL]
MOVING HEAD TO MAKE IT POINT TO THE NEW 1st NODE
Third: head->[data||Pointer pointing to Previous 1st node]->[data||NULL]
Isn't it cowardly behaviour to downvote without a valid reason?
I'm trying to insert a node dynamically between two indices which hold struct of type Node. The first element in array is head pointer and second element is tail.
I'm trying to dynamically grow the double linkedlist between the two indices of the array. Following is the code I've tried so far.
I could have created head and tail as an node dynamically as well but as per the requirement I've to do like this.
It is guarenteed to have the node which is to be inserted data value in between the value of qllentry[0].data and qllentry[1].data
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
struct Node {
int data;
struct Node *qprev;
struct Node *qnext;
}Node;
struct Node qllentry[2];
int main()
{
struct Node head, tail;
head.data = INT_MAX;
tail.data = INT_MIN;
head.qnext = &tail;
tail.qprev = &head;
head.qprev = NULL;
tail.qnext = NULL;
qllentry[0] = head;
qllentry[1] = tail;
int key = 20;
struct Node *curr ;
struct Node *prev;
curr= &qllentry[0];
while(curr->qnext != NULL && curr->data >= key) {
curr = curr->qnext;
}
prev = curr->qprev;
struct Node *new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = key;
new_node->qnext = prev->qnext;
prev->qnext = new_node;
new_node->qprev = prev;
if (new_node->qnext != NULL)
new_node->qnext->qprev = new_node;
return 0;
}
The insertion of the new node isn't happening between the head and tail indices as expected. I've added few print statements for debugging
Any help is appreciated.
While there is nothing wrong with keeping an array (or for that matter a pointer) that points to the head and tail of your list, if you use an array, after assigning the address keep your array references out of your list operations. Mixing &array[x] in with your list operations does nothing but cause confusion. When working with the list, treat it as a list and forget about the array.
Your primary problem is you iterate one node to far looking for where to insert the new_node resulting in you iterating to tail before you stop. Stop your iteration on the node before you insert new_node. You do this by testing for:
/* test curr->qnext->data > key to stop before tail */
while (curr->qnext && curr->qnext->data > key)
curr = curr->qnext;
(note: masking levels of indirection with a variable like you do next with prev = curr->qprev; just hides details -- which can add to confusion later on. It's perfectly legal, but use with discretion...)
Now you can concentrate on inserting new_node between &head and &tail where it needs to go.
In any list insertion, you are simply re-wiring the pointer->next of the current node to point to the new_node and the pointer->prev of the next node to point to new_node. To finish the insertion your new_node->qprev points to curr and new_node->qnext points to curr->next, e.g.
new_node->qprev = curr; /* rewire pointers */
new_node->qnext = curr->qnext;
curr->qnext->qprev = new_node;
curr->qnext = new_node;
(note: the easy way to figure it out is to pull at a piece of paper and a No. 2 pencil and draw a block for curr a block for new_node and a block for tail and then draw lines for prev/next pointers (for both the list without the new_node and with it). Then, with the logic straight, sit down to the keyboard and pecking it out.)
Further, you must always validate your allocations, e.g.
/* allocate and VALIDATE! */
if (!(new_node = malloc (sizeof *new_node))) {
perror ("malloc - new_node");
exit (EXIT_FAILURE);
}
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. So if you allocate it, keep track of a pointer to the block and free when you are done with it. For example, when done outputting the list values (or in a dedicated loop), you can free the memory you allocate similar to:
curr = &head; /* output list */
while (curr) {
printf ("%d\n", curr->data);
struct Node *victim = curr; /* self-explanatory */
curr = curr->qnext;
/* do not forget to free allocated memory */
if (victim != &head && victim != &tail) {
free (victim);
}
}
Putting it altogether, you can do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
struct Node {
int data;
struct Node *qprev;
struct Node *qnext;
} Node;
struct Node qllentry[2];
int main (void) {
struct Node head = { .data = INT_MAX },
tail = { .data = INT_MIN },
*curr,
*new_node;
qllentry[0] = head; /* keep your array and list operations separate */
qllentry[1] = tail;
head.qnext = &tail; /* begin list operations */
tail.qprev = &head;
int key = 20;
curr = &head;
/* test curr->qnext->data > key to stop before tail */
while (curr->qnext && curr->qnext->data > key)
curr = curr->qnext;
/* allocate and VALIDATE! */
if (!(new_node = malloc (sizeof *new_node))) {
perror ("malloc - new_node");
exit (EXIT_FAILURE);
}
new_node->data = key; /* assign value to new_node */
new_node->qprev = curr; /* rewire pointers */
new_node->qnext = curr->qnext;
curr->qnext->qprev = new_node;
curr->qnext = new_node;
curr = &head; /* output list */
while (curr) {
printf ("%d\n", curr->data);
struct Node *victim = curr; /* self-explanatory */
curr = curr->qnext;
/* do not forget to free allocated memory */
if (victim != &head && victim != &tail) {
free (victim);
}
}
return 0;
}
Example Use/Output
$ ./bin/llarray
2147483647
20
-2147483648
Memory Use/Error Check
It is imperative that you use a memory error checking program to insure 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/llarray
==8665== Memcheck, a memory error detector
==8665== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8665== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==8665== Command: ./bin/llarray
==8665==
2147483647
20
-2147483648
==8665==
==8665== HEAP SUMMARY:
==8665== in use at exit: 0 bytes in 0 blocks
==8665== total heap usage: 1 allocs, 1 frees, 24 bytes allocated
==8665==
==8665== All heap blocks were freed -- no leaks are possible
==8665==
==8665== For counts of detected and suppressed errors, rerun with: -v
==8665== 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.
Simple Pointer Dump/Check
Lastly, in addition to stepping though the addresses with a debugger, you can always write a short debug routing to help you pick out if, and where, you have any problems with your pointer handling. (you don't have to output anything at all, you can just check the addresses with an equality if you like) This lets you look at all pointers at once. Just a simple routing to output your node pointers is often helpful. All you need is, e.g.
void debugptrs (struct Node *list)
{
printf ("list pointers:\n\n");
for (struct Node *iter = list; iter; iter = iter->qnext)
printf ("prev: %16p curr: %16p next: %16p\n",
(void*)iter->qprev, (void*)iter, (void*)iter->qnext);
putchar ('\n');
}
Which would provide output similar to:
$ ./bin/llarray
list pointers:
prev: (nil) curr: 0x7ffd56371910 next: 0x1038010
prev: 0x7ffd56371910 curr: 0x1038010 next: 0x7ffd56371930
prev: 0x1038010 curr: 0x7ffd56371930 next: (nil)
I always found it helpful just to visually traverse the address from head to tail and back. If any prev or next for a node isn't what is output as the address for that node on the previous (or next) line, you know where you problem is.
Look things over and let me know if you have further questions.
Following is the code with some modification based on the code from the question, it prints the result as expected I guess:
dlink.c:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
struct Node {
int data;
struct Node *qprev;
struct Node *qnext;
} Snode;
int main() {
struct Node *head = (struct Node*)malloc(sizeof(struct Node));
struct Node *tail = (struct Node*)malloc(sizeof(struct Node));
// init head,
head->data = INT_MAX;
head->qnext = tail;
head->qprev = NULL;
// init tail,
tail->data = INT_MIN;
tail->qprev = head;
tail->qnext = NULL;
int key = 20;
struct Node *curr = head;
struct Node *prev;
//get the pointer of the process which has less priority than the current process
while(curr->data >= key && curr->qnext != NULL) {
curr = curr->qnext;
}
prev = curr->qprev;
printf("head %p, data is %d, next is %p, prev is %p\n", head, head->data, (void *)head->qnext, (void *)head->qprev);
printf("tail %p, data is %d, next is %p, prev is %p\n", tail, tail->data, (void *)tail->qnext, (void *)tail->qprev);
printf("prev of new node %p, data is %d, next is %p, prev is %p\n", prev, prev->data, (void *)prev->qnext, (void *) prev->qprev);
printf("--------------------\n\n");
struct Node *new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = key;
new_node->qnext = prev->qnext;
prev->qnext = new_node;
new_node->qprev = prev;
if (new_node->qnext != NULL)
new_node->qnext->qprev = new_node;
else
tail = new_node;
printf("head %p, data is %d, next is %p, prev is %p\n", head, head->data, (void *)head->qnext, (void *)head->qprev);
printf("new_node %p, data is %d, next is %p, prev is %p\n", new_node, new_node->data, (void *)new_node->qnext, (void *)new_node->qprev);
printf("tail %p, data is %d, next is %p, prev is %p\n", tail, tail->data, (void *)tail->qnext, (void *)tail->qprev);
return 0;
}
The running result:
head 0x2380010, data is 2147483647, next is 0x2380030, prev is (nil)
tail 0x2380030, data is -2147483648, next is (nil), prev is 0x2380010
prev of new node 0x2380010, data is 2147483647, next is 0x2380030, prev is (nil) // this is same as head,
--------------------
head 0x2380010, data is 2147483647, next is 0x2380460, prev is (nil)
new_node 0x2380460, data is 20, next is 0x2380030, prev is 0x2380010
tail 0x2380030, data is -2147483648, next is (nil), prev is 0x2380460
Suggestions
Don't mix struct (head, tail) & struct pointer (new_node), it's confusing, and easy to make mistake.
A singly linked list could be enough to do such insert, there is a tricky way to insert element in a singly linked list.
To get good performance, you can allocate a large cache, then create new node from the cache.
When compile your c code, add -Wall options, which will give you more warnings.
As my many previous posts show, I am making a code to simulate a crazy 8's card game. I have a delete node function that is meant to delete the card from the deck being played. It works for cards after the first, but every time i try to delete the first card (node) from the list it will not delete and then messes up the whole program after it. Here is the function:
void deleteNode(card *head, int coordinate) {
card *current = head;
card *temp = NULL;
temp = current;
int count = 1;
while (head != NULL) {
if (coordinate == 0) {
current = current->listp;
free(temp);
break;
}
else if (count == coordinate) {
temp = current->listp;
current->listp = current->listp->listp;
free(temp);
break;
}
else {
count++;
current = current->listp;
}
}
}
The *head passed into it is the top of the hand being played. The coordinate is the number of card the user wants to play. For example, if the first card in their deck is Q of Hearts and that's what they want to play, they would enter one. In the function call, I subtract one from the user choice so it will match up with the list (since the list starts at 0). Any help would be appreciated. I can't move on with my project until I get this problem resolved!
As mentioned in the comments, the problem you are running into is due to not passing the address-of the list to the deleteNode function. The problem is basic, but it catches a lot of people. The address of a linked-list, is the address of the first node. So when deleting the first node, you must set a new list address to the next node address in order for the list to continue to operate.
When you pass a pointer to a function, e.g. void deleteNode(card *head, ..., the function deleteNode receives a copy of the pointer head. The copy has an address all its own and that address has no relation to the address of the pointer back in the calling function. The value of the pointer is the same in deleteNode as it is in the caller, but the address is completely different.
The problem occurs when you delete the first node in deleteNode. The memory is free'd, and the deleteNode function returns. Now back in the caller (presumably main()), the next time you attempt to access head -- bam! segfault. Why? The address for head was never updated in main, so it still points to the original node -- and what did you just do to the memory for the original node in deleteNode? (you called free on a pointer that pointed to the block of memory holding the first node -- its gone...)
To fix the problem, simply pass the address-of the list (head) to deleteNode. (e.g. void deleteNode(card **head, ...). Then you are operating on the address of head (e.g. a pointer-to-the-pointer-head). Now before deleting the first node you can set *head = head->listp; and have the new list address reflected back in the calling function (main()). For example, your code could be written as:
void delnode (card **head, int coordinate)
{
card *current = *head;
card *victim = NULL;
victim = current;
int count = 1;
while (current != NULL) {
if (coordinate == 0) {
*head = current->listp;
free (victim);
break;
}
else if (count == coordinate) {
victim = current->listp;
current->listp = current->listp->listp;
free (victim);
break;
}
else {
count++;
current = current->listp;
}
}
}
However, you can make a few improvements to the logic of the function, with minimal effort. e.g.
void delnode (card **head, int coordinate)
{
card *current = *head;
card *victim = current;
int count = 1;
if (coordinate == 0) {
*head = current->listp;
free (victim);
return;
}
while (current != NULL)
{
if (count == coordinate) {
victim = current->listp;
current->listp = current->listp->listp;
free (victim);
return;
}
count++;
current = current->listp;
}
}
Lastly, visit the links describing How to Ask a Question and How to create a Minimal, Complete, and Verifiable example. Providing the necessary details, including your code, and associated errors, if any, will allow everyone here to help you with your question.
This question is a perfect example. For anyone to help you and actually compile and confirm the problem or answer, you are asking the folks here to write a sample program that makes an educated guess at what your underlying list structure presumably is. When you ask a question here, the purpose of providing a MCVE is so that others may compile your code and confirm the problem you are having, and if need be, run the compiled code through a debugger in order to help you. You will get much more help and much more of a positive response if you follow the minimal suggestions and rule of the site that are there to help us help you.
That being said, you can confirm the operation of your delete with this small bit of sample code.
#include <stdio.h>
#include <stdlib.h>
typedef struct card {
int cardno;
struct card *listp;
} card;
card *createnode (int c);
card *insert (card **list, int c);
void prnlist (card *list);
void delnode (card **head, int coordinate);
void dellist (card *list);
void *xcalloc (size_t nmemb, size_t sz);
int main (void) {
card *list = NULL;
insert (&list, 18); /* insert test nodes */
insert (&list, 6);
insert (&list, 54);
insert (&list, 12);
insert (&list, 60);
insert (&list, 30);
printf ("\noriginal list:\n");
prnlist (list);
printf ("\ndeleting node: 2\ndeleting node: 0\n");
delnode (&list, 2); /* delete 3rd & 1st nodes */
delnode (&list, 0);
printf ("\nfinal list:\n");
prnlist (list);
dellist (list); /* free allocated memory */
return 0;
}
card *createnode (int c)
{
card *node = xcalloc (1, sizeof *node);
node->listp = NULL;
node->cardno = c;
return node;
}
card *insert (card **list, int c)
{
card *iter = *list;
card *node = createnode (c);
if (!*list) { /* add 1st node to list */
*list = node;
return *list;
}
/* insert all other nodes at end */
for (; iter->listp; iter = iter->listp) {}
iter->listp = node;
return *list;
}
void prnlist (card *list)
{
card *iter = list;
for (; iter->listp; iter = iter->listp)
printf (" cardno : %d\n", iter->cardno);
printf (" cardno : %d\n", iter->cardno);
}
void delnode (card **head, int coordinate)
{
card *current = *head;
card *victim = current;
int count = 1;
if (coordinate == 0) {
*head = current->listp;
free (victim);
return;
}
while (current != NULL)
{
if (count == coordinate) {
victim = current->listp;
current->listp = current->listp->listp;
free (victim);
return;
}
count++;
current = current->listp;
}
}
void dellist (card *list)
{
card *iter = list;
while (iter) {
card *victim = iter;
iter = iter->listp;
free (victim);
}
}
void *xcalloc (size_t nmemb, size_t sz)
{
void *memptr = calloc (nmemb, sz);
if (!memptr) {
fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
return memptr;
}
Example Use/Output
$ ./bin/lldelcard
original list:
cardno : 18
cardno : 6
cardno : 54
cardno : 12
cardno : 60
cardno : 30
deleting node: 2
deleting node: 0
final list:
cardno : 6
cardno : 12
cardno : 60
cardno : 30
Memory Error Check
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves 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 insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.
For Linux valgrind is the normal choice. There are many subtle ways to misuse a new block of memory. Using a memory error checker allows you to identify any problems and validate proper use of of the memory you allocate rather than finding out a problem exists through a segfault. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/lldelcard
==9094== Memcheck, a memory error detector
==9094== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==9094== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==9094== Command: ./bin/lldelcard
==9094==
original list:
cardno : 18
cardno : 6
cardno : 54
cardno : 12
cardno : 60
cardno : 30
deleting node: 2
deleting node: 0
final list:
cardno : 6
cardno : 12
cardno : 60
cardno : 30
==9094==
==9094== HEAP SUMMARY:
==9094== in use at exit: 0 bytes in 0 blocks
==9094== total heap usage: 6 allocs, 6 frees, 96 bytes allocated
==9094==
==9094== All heap blocks were freed -- no leaks are possible
==9094==
==9094== For counts of detected and suppressed errors, rerun with: -v
==9094== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Good luck with your coding.