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.
Related
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.
I wanted to write a program that removes all occurrences of a number from a simple linked list using recursion, so I tried but I had problem: the program that I have written erases all the occurrences in the list but it does not delete the one that exists at the beginning (the occurrence that exists at the first node), here is the code in C:
typedef struct list {
int data;
struct list *next;
} list;
list *delete(int x, list *head) {
if (head->next == NULL)
return head;
list *newnode = delete(x, head->next);
if (newnode->data == x) {
head->next = head->next->next;
free(newnode);
}
return head;
}
I wish someone can help me to improve my algorithm, THANKS IN ADVANCE.
There are multiple problems in the code:
You dereference head without first checking if it is NULL. The function cannot handle empty lists.
You explicitly return the head node without testing its value if the list has a single element.
You only test the second element of the list after recursing on head->next. Hence the first element is never tested.
Here is a modified version that just tests the first node and recurses for the rest of the list:
list *delete(int x, list *head) {
if (head == NULL)
return head;
if (head->data == x) {
list *node = head;
head = head->next;
free(node);
return delete(x, head);
}
head->next = delete(x, head->next);
return head;
}
This code:
if(head->next == NULL)
return head;
explicitly makes the function return any 1-element list unchanged. That creates the problem you describe, so that makes no sense to have there.
I guess it should be possible to formulate the deletion of a list element recursively, although it certainly is not a common/typical/good way to do it.
This might work, not tested:
list * delete(list *head, int value)
{
if (head == NULL)
return NULL;
if (head->data == value)
{
list * tail = head->next;
free(head);
return delete(tail, value);
}
// List was not empty and did not start with the value,
// so set the tail of the list to the tail without the value.
head->next = delete(head->next, value);
return head;
}
without it manages to delete the one that exists at the beginning (the occurrence that exists at the first node),
this is because of these lines :
if(head->next == NULL)
return head;
when there is only one element you return without managing the fact it can contains the data to remove
You do not need to have a recursive definition of delete, and worst using non terminal recursion.
Can be also adding working functions to check the execution :
#include <stdio.h>
#include <stdlib.h>
typedef struct list {
int data;
struct list* next;
} list;
list* delete(int x, list* head)
{
list ** p = &head;
while (*p != NULL) {
if ((*p)->data == x) {
list * d = *p;
*p = (*p)->next;
free(d);
}
else
p = &(*p)->next;
}
return head;
}
void print(list * l)
{
if (l == NULL)
puts("<empty>");
else {
do {
printf("%d ", l->data);
l = l->next;
} while (l != NULL);
putchar('\n');
}
}
list * make(int data, list * next)
{
list * l = malloc(sizeof(list));
l->data = data;
l->next = next;
return l;
}
int main(int argc, char ** argv)
{
list * head = make(1, make(2, make(1, NULL)));
print(head);
head = delete(1, head);
print(head);
head = delete(2, head);
print(head);
return 0;
}
Compilation and execution:
/tmp % gcc -Wall l.c
/tmp % ./a.out
1 2 1
2
<empty>
/tmp %
Under valgrind :
/tmp % valgrind ./a.out ==14732== Memcheck, a memory error detector
==14732== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14732== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==14732== Command: ./a.out
==14732==
1 2 1
2
<empty>
==14732==
==14732== HEAP SUMMARY:
==14732== in use at exit: 0 bytes in 0 blocks
==14732== total heap usage: 3 allocs, 3 frees, 48 bytes allocated
==14732==
==14732== All heap blocks were freed -- no leaks are possible
==14732==
==14732== For counts of detected and suppressed errors, rerun with: -v
==14732== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
/tmp %
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
So I have code that I will show below
struct GraphicElement {
char* fileName;
struct GraphicElement* pNext;
};
struct RasterGraphic {
struct GraphicElement* GraphicElements;
};
AND ALSO
void InsertGraphicElement(struct RasterGraphic* pA)
{
int counter = 1;
int response = 0;
char tempString[256];
struct GraphicElement *newNode = malloc(sizeof(*newNode));
if (newNode == NULL) return;
newNode->fileName = malloc(256 * sizeof(char));
if (newNode->fileName == NULL) return;
newNode->pNext = NULL;
printf("Insert a GraphicElement in the RasterGraphic\nPlease enter the GraphicElement filename: ");
scanf("%s", newNode->fileName);
if (pA->GraphicElements == NULL)
{
pA->GraphicElements = newNode;
printf("This is the first GraphicElement in the list\n");
}
else
{
struct GraphicElement *tempHead = pA->GraphicElements;
while (tempHead->pNext != NULL)
{
tempHead = tempHead->pNext;
counter++;
}
printf("There are %d GraphicElement(s) in the list. Please specify the position (<= %d) to insert at :", counter, counter);
scanf("%d", &response);
if (response == counter) {
tempHead->pNext = newNode;
return;
}
}
return;
}
So as you can see I have a struct definition and then a function to insert a node into the list. I have it so that if its the first node it inserts and tells the user it is the first element in the list. Where I am having a problem is adding more elements. Right now the code has no problems adding new nodes in sequence. The user is asked where they would like to insert into the list. Right now as long as they choose to just add it in sequence it works perfectly. I've tried a lot of things i just cant figure out the logic to loop through and be able to add a node to the middle of the list so an example output would be like this..
List contains 1, 2, 3
the user adds a fourth element 4 but wants to add it to spot 1 which is where the 2 is sitting so the new updated list would look like 1, 4, 2, 3.
There is no magic to inserting anywhere within the linked list, the only real challenge is keeping your pointers straight when handling three conditions:
inserting the first node (or new first node);
inserting a node between at some position between the existing first and last node; and
inserting at the end of the list.
The first case, inserting the first (or new first) node simply requires allocating a new node and either inserting it is the first node in the list, or if the head node already exists, setting newnode->next = head; and head = newnode;
Inserting at the end is not much different, you just iterate to the last node and set last->next = newnode;
The case of inserting a node in between takes a bit more thought. The easiest way to keep your pointers straight is to pull out a pencil and paper and draw out your pointer diagram of your list and then break the list in two where you need to insert the new node, and figure out the steps required before you pickup the keyboard (it will go a lot easier that way)
Nothing fancy is needed, just a block diagram with your next (or your pNext) pointer connecting the nodes. A simple list will two nodes (A and B) will do, e.g.
A B
+------+ +------+
| node | | node |
| next |--> | next |-->NULL
+------+ +------+
Then just break the list at the point where the new node will be inserted, e.g.
A B
+------+ | +------+
| node | / | node |
| next |--> \ | next |-->NULL
+------+ / +------+
|
new
+------+
| node |
| next |-->
+------+
That allows you to visualize the needed steps. Locate node A, set new->next = B;, set A->next = new; (note: you must stop at the node before the location you wish to insert the new node) The result will then be:
A new B
+------+ +------+ +------+
| node | | node | | node |
| next |--> | next |--> | next |-->NULL
+------+ +------+ +------+
When an understanding of exactly what you need to do to handle the insertion, now pick up the keyboard and implement that logic.
Since you will have a single insert function to handle all three cases, as #WhozCraig commented, when handling list operations (or pointers in general where you may need to assign a new block of memory within a function changing the address of the pointer), it helps to pass the address of the pointer to your function (so that your function receives the pointer itself -- instead of a copy of the pointer).
Whether in your case you need to initially allocate for the list wrapper, or in a simple list where you may assign a new node as the first node in the list, passing the address of the pointer allows the change within the function without having to return and assign the return as the new address back in the calling function.
Adding a couple of typedefs gelement_t for your struct GraphicElement and rgraphic_t for struct RasterGraphic types, both to cut down on typing an remove some of the MixedCase style awkwardness, you can think about your InsertGraphicElement function in the following way.
First, your InsertGraphicElement function will either succeed or fail, so choose a meaningful return type that can indicate success or failure. When dealing with lists, returning a pointer to the newly inserted node is helpful and allows a return of NULL in the even of failure, e.g.
gelement_t *InsertGraphicElement (rgraphic_t **pA)
Since you are passing a pointer to pointer to your RasterGraphic struct, you can add data to the struct to make it a bit more useful as a wrapper to your actual list. Adding a node-counter is a convenient way to keep track of the number of nodes in a singly-linked list without having to iterate over the list each time, e.g.
typedef struct RasterGraphic {
size_t nelements;
gelement_t *GraphicElements;
} rgraphic_t;
Within your function you should validate that you have an allocated pointer, and if not, then allocate for the RasterGraphic struct, e.g.
int position = 0;
if (!*pA) { /* if list NULL - allocate new list */
puts ("allocating new list.");
*pA = malloc (sizeof **pA);
if (!*pA) { /* validate every allocation */
perror ("malloc-*list");
exit (EXIT_FAILURE);
}
(*pA)->nelements = 0; /* initialize values */
(*pA)->GraphicElements = NULL;
}
Next you can allocate and validate the new node to add either as the first node or at position, e.g.
gelement_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate */
perror ("malloc-node");
exit (EXIT_FAILURE);
}
Next collect your filename and position information and set node->fileName to a newly allocated block holding the filename entered by the user (a couple of helper-functions make this much easier), e.g.
node->fileName = get_filename_stdin(); /* request filename */
if (!node->fileName) { /* validate */
free (node);
return NULL;
}
node->pNext = NULL; /* set next pointer NULL */
position = get_int_stdin (*pA); /* request position */
You are now at the point within the function where you handle the 3-cases identified at the beginning of the answer. If there is no (*pA)->GraphicElements node yet, you will add the first node (so you don't need to ask for position). If it isn't the first node, and the position requested is 0, you insert as a new first node. (both can be handled in a single case)
If the position requested is greater than zero, then you iterate to the node before the insertion point and insert as indicated with the diagram above and insert the node there.
One approach would be:
gelement_t *p = (*pA)->GraphicElements;
if (!p || position == 0) { /* insert as new head */
node->pNext = p;
(*pA)->GraphicElements = node;
}
else { /* insert at position (default end) */
int n = 0;
while (n < position - 1 && p->pNext) { /* locate node before */
p = p->pNext;
n++;
}
node->pNext = p->pNext; /* set node->pNext to current pNext */
p->pNext = node; /* set current pNext to node */
}
(*pA)->nelements++; /* increment number of elements in list */
Which will handle your insertion based on the user input. All that remains is to:
return node; /* meaningful return to indicate success/failure */
}
(note: when you are comfortable with the list operation logic, it helps to break this function up into several functions that handle the operations individually, like create_list(), create_node() and add_node(). (where you create your RasterGraphic list, your GraphicElement node and finally add that node at a given position in the list -- that is left to you)
Putting it altogether and adding the helper functions, a short example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> /* for PATH_MAX */
typedef struct GraphicElement {
char* fileName;
struct GraphicElement* pNext;
} gelement_t;
typedef struct RasterGraphic {
size_t nelements;
gelement_t *GraphicElements;
} rgraphic_t;
char *get_filename_stdin (void)
{
char filename[PATH_MAX] = "";
char *newname = NULL;
fputs ("enter filename : ", stdout);
if (fgets (filename, PATH_MAX, stdin)) {
size_t len = strlen (filename);
if (len && filename[len-1] == '\n') {
filename[--len] = 0;
if (!len) {
fputs ("error: filename empty.\n", stderr);
return NULL;
}
}
else if (len == PATH_MAX) {
fputs ("error: filename exceeds PATH_MAX\n", stderr);
return NULL;
}
newname = malloc (len + 1);
if (!newname) {
perror ("malloc-newname");
exit (EXIT_FAILURE);
}
memcpy (newname, filename, len + 1);
}
return newname;
}
int get_int_stdin (rgraphic_t *list)
{
char buf[PATH_MAX];
int pos = 0;
if (!list->nelements) {
puts ("inserting as head.");
return 0;
}
fputs ("index to insert: ", stdout);
if (fgets (buf, PATH_MAX, stdin))
if (sscanf (buf, "%d", &pos) != 1 || pos < 0 ||
pos > (long)list->nelements)
return list->nelements;
return pos;
}
gelement_t *InsertGraphicElement (rgraphic_t **pA)
{
int position = 0;
if (!*pA) { /* if list NULL - allocate new list */
puts ("allocating new list.");
*pA = malloc (sizeof **pA);
if (!*pA) { /* validate every allocation */
perror ("malloc-*list");
exit (EXIT_FAILURE);
}
(*pA)->nelements = 0; /* initialize values */
(*pA)->GraphicElements = NULL;
}
gelement_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate */
perror ("malloc-node");
exit (EXIT_FAILURE);
}
node->fileName = get_filename_stdin(); /* request filename */
if (!node->fileName) { /* validate */
free (node);
return NULL;
}
node->pNext = NULL; /* set next pointer NULL */
position = get_int_stdin (*pA); /* request position */
gelement_t *p = (*pA)->GraphicElements;
if (!p || position == 0) { /* insert as new head */
node->pNext = p;
(*pA)->GraphicElements = node;
}
else { /* insert at position (default end) */
int n = 0;
while (n < position - 1 && p->pNext) { /* locate node before */
p = p->pNext;
n++;
}
node->pNext = p->pNext; /* set node->pNext to current pNext */
p->pNext = node; /* set current pNext to node */
}
(*pA)->nelements++; /* increment number of elements in list */
return node; /* meaningful return to indicate success/failure */
}
/* loop over list printing values */
void prn_list (rgraphic_t *list)
{
size_t n = 0;
gelement_t *node = list->GraphicElements;
printf ("\n\n%zu nodes in list\n", list->nelements);
for (; node; node = node->pNext)
printf ("%2zu: %s\n", 1 + n++, node->fileName);
}
/* loop over list freeing memory (pay attention to victim) */
void free_list (rgraphic_t *list)
{
gelement_t *node = list->GraphicElements;
while (node) {
gelement_t *victim = node;
node = node->pNext;
free (victim->fileName);
free (victim);
}
free (list);
}
int main (void) {
rgraphic_t *list = NULL;
puts ("\nNOTE: pressing [Enter] for index - inserts at end!\n"
" [Ctrl+d] at \"filename: \" prompt to end input.\n");
while (InsertGraphicElement(&list)) {} /* create list/insert nodes */
prn_list (list); /* print list */
free_list (list); /* free list */
}
Example Use/Output
$ ./bin/ll_single_insert_ptp
NOTE: pressing [Enter] for index - inserts at end!
[Ctrl+d] at "filename: " prompt to end input.
allocating new list.
enter filename : one
inserting as head.
enter filename : four
index to insert: 1
enter filename : two
index to insert: 1
enter filename : three
index to insert: 2
enter filename : five
index to insert:
enter filename :
5 nodes in list
1: one
2: two
3: three
4: four
5: five
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/ll_single_insert_ptp
==9747== Memcheck, a memory error detector
==9747== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==9747== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==9747== Command: ./bin/ll_single_insert_ptp
==9747==
NOTE: pressing [Enter] for index - inserts at end!
[Ctrl+d] at "filename: " prompt to end input.
allocating new list.
enter filename : one
inserting as head.
enter filename : four
index to insert: 1
enter filename : two
index to insert: 1
enter filename : three
index to insert: 2
enter filename : five
index to insert:
enter filename :
5 nodes in list
1: one
2: two
3: three
4: four
5: five
==9747==
==9747== HEAP SUMMARY:
==9747== in use at exit: 0 bytes in 0 blocks
==9747== total heap usage: 12 allocs, 12 frees, 136 bytes allocated
==9747==
==9747== All heap blocks were freed -- no leaks are possible
==9747==
==9747== For counts of detected and suppressed errors, rerun with: -v
==9747== 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.
I want to create a linked list using recursion. After executing the code, I get only the value of the first node and rest are not being printed.
#include<stdio.h>
#include<malloc.h>
typedef struct node NODE;
struct node
{
int data;
struct node *next;
} *start=NULL,*ptr;
void display();
int create(int);
int main()
{
int n;
printf("\nEnter the no of node?\t");
scanf("%d",&n);
create(n);
display();
}
int create(int x)
{
if(x==0)
return;
else{
NODE *node;
node=((NODE *)malloc(sizeof(NODE)));
printf("Enter the data:\n");
scanf("%d",&node->data);
node->next=NULL;
if(start==NULL)
{
ptr=start=node;
}
else
{
ptr=node->next;
ptr=node;
}
ptr->next=NULL;
}
create(x-1);
}
void display()
{
NODE *ds;
ds=start;
while(ds!=NULL)
{
printf("%d->",ds->data);
ds=ds->next;
}
}
I think the problem is when i call create(x-1);, but I am not sure.
Is my logic correct? Can someone pin-point my mistake?
Try changing the logic,
int create(int x) {
if (x == 0)
return 0;
else {
NODE *node;
node = ((NODE *) malloc(sizeof (NODE)));
printf("Enter the data:\n");
scanf("%d", &node->data);
node->next = NULL;
if (start == NULL) {
ptr = start = node;
} else {
//ptr = node->next;
ptr->next = node;
ptr = node;
}
ptr->next = NULL;
}
create(x - 1);
}
You are not resetting the head correctly.
Also good to check this implemetation out > http://geeksquiz.com/linked-list-set-1-introduction/
The significant error leading to your problem was your assignment of pointers in create(int). You were assigning the first pointer correctly, but then assigning NULL to all remaining pointers. There are several ways to handle this, but a clean and straightforward way is to only advance ptr=ptr->next within the else block as follows:
if (start == NULL)
{
ptr = start = node;
}
else
{
ptr->next = node;
ptr = ptr->next;
}
You are dynamically allocating memory, so this means you are responsible for tracking its use, preserving a pointer to the starting block of each allocation, and finally freeing the memory when it is no longer in use. Start now. Get in the habit of handling your memory cleanup whenever you allocate, and don't simply rely on the program exit to do it for you. While it may seem trivial now, when you begin handling functions with multiple allocations, etc., if you have not developed good habits in this regard, your code will likely leak memory like a sieve. A simple cleanup function could be nothing more than:
void destroy()
{
if (!start) return;
NODE *ds = start;
while (ds != NULL)
{
NODE *victim = ds;
ds = ds->next;
free (victim);
}
}
The malloc issue. malloc returns the starting address for the block of memory allocated, there is no need to cast the return in C. When you are allocating memory for data types you have just declared, use the variable with sizeof instead of the datatype. e.g.:
NODE *node;
node = malloc (sizeof *node);
instead of
node = malloc (sizeof (NODE));
This will become apparent when dealing with pointers to pointers, etc. It makes far more sense to operate on your variable than it does to remember whether you are allocating for NODE* or NODE**. This is especially true when the allocation is many lines below the declaration in your code or when receiving the pointer in a function argument list.
Additionally, you need to validate the return from malloc each time you allocate memory to insure you haven't exhausted the available memory. e.g.:
NODE *node;
if (!(node = malloc (sizeof *node))) {
fprintf (stderr, "error: virtual memory exhausted\n");
exit (EXIT_FAILURE);
}
Finally, putting it all together, one approach to your problem would be:
#include <stdio.h>
#include <stdlib.h> /* for exit & EXIT_FAILURE */
typedef struct node NODE;
struct node {
int data;
struct node *next;
} *start=NULL,*ptr;
void display();
void create (int);
void destroy();
int main (void)
{
int n;
printf ("\nEnter the no of node: ");
scanf ("%d",&n);
create (n);
display();
destroy();
return 0;
}
void create (int x)
{
if (x == 0) return;
NODE *node;
if (!(node = malloc (sizeof *node))) {
fprintf (stderr, "error: virtual memory exhausted\n");
exit (EXIT_FAILURE);
}
printf ("Enter the data: ");
scanf ("%d",&node->data);
node->next = NULL;
if (start == NULL)
{
ptr = start = node;
}
else
{
ptr->next = node;
ptr = ptr->next;
}
create (x-1);
}
void display()
{
if (!start) return;
NODE *ds = start;
while (ds != NULL)
{
if (ds == start)
printf ("%d", ds->data);
else
printf("->%d", ds->data);
ds = ds->next;
}
printf ("\n");
}
void destroy()
{
if (!start) return;
NODE *ds = start;
while (ds != NULL)
{
NODE *victim = ds;
ds = ds->next;
free (victim);
}
}
Example
$ ./bin/llrecurse
Enter the no of node: 4
Enter the data: 2
Enter the data: 4
Enter the data: 6
Enter the data: 8
2->4->6->8
Use a Memory Checker
Regardless of your platform, it is good to use a memory checker, like valgrind on Linux to check for memory errors and insure you have freed all the memory you have allocated. A memory checker, not only provides a confirmation that all memory has been freed, it will also report on subtle errors in the way you attempt to access the memory you have allocated which can alert you to issues that can bite you later. It is simple to use, simply:
$ valgrind ./bin/llrecurse
==17434== Memcheck, a memory error detector
==17434== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==17434== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==17434== Command: ./bin/llrecurse
==17434==
Enter the no of node: 4
Enter the data: 2
Enter the data: 4
Enter the data: 6
Enter the data: 8
2->4->6->8
==17434==
==17434== HEAP SUMMARY:
==17434== in use at exit: 0 bytes in 0 blocks
==17434== total heap usage: 4 allocs, 4 frees, 64 bytes allocated
==17434==
==17434== All heap blocks were freed -- no leaks are possible
==17434==
==17434== For counts of detected and suppressed errors, rerun with: -v
==17434== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
That should get you started, and if you learn the good habits early, managing memory will be a whole lot easier as you get further into programming in C.
When you're doing this:
ptr=node->next;
ptr=node;
You're loosing your reference to the tail of the list, and therefore not adding node to the list. You should be doing this:
ptr->next = node;
ptr = ptr->next;
This points the next pointer of the current tail to the new node, then moves ptr down to the new tail.
Also, the ptr->next=NULL at the end of the loop is unnecessary, since ptr is now the same as node, and you already did node->next = NULL.
Here is a method
typedef struct Node {
int data;
struct Node *pNext;
} Node;
Write a mkNode to handle malloc
Node *mkNode(int value, Node *pNext) {
Node *pNode = malloc(sizeof(Node));
if (pNode == NULL) {
printf("error");
exit(-1);
}
pNode->data = value;
pNode->pNext = pNext;
return pNode;
};
create linked list
Node *rec_create_list_from_arr(int *arr, int len, int i) {
if (i == len) {
return NULL;
}
return mkNode(arr[i], rec_create_list_from_arr(arr, len, i + 1));
}
free
void freeList(struct Node *head) {
if (head != NULL) {
freeList(head->pNext);
free(head);
}
}
test
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
Node *l = rec_create_list_from_arr(arr, 9, 0);
freeList(l);
return 0;
}
valgrind says it's ok
❯ valgrind --leak-check=full --show-leak-kinds=all ./tmp
==630325== Memcheck, a memory error detector
==630325== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==630325== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==630325== Command: ./tmp
==630325==
123456789
1233456789
1233456789
==630325==
==630325== HEAP SUMMARY:
==630325== in use at exit: 0 bytes in 0 blocks
==630325== total heap usage: 11 allocs, 11 frees, 1,184 bytes allocated
==630325==
==630325== All heap blocks were freed -- no leaks are possible
==630325==
==630325== For lists of detected and suppressed errors, rerun with: -s
==630325== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)