I want to create a list with the following structure:
list.h: Contains function prototypes and defines data types
lt.c: main function to test the list
list.c: actual implementation of the list
When executing it I always get a segfault error. When trying to identify it with gdb it is shown that it is the fault of the following line in lt.c:
list_t *li=list_init();
The rest of my lt.c file looks as follows:
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
int main ( int argc, char *argv [ ], char *envp [ ] )
{
list_t *li=list_init();
//li=list_init();
/* if((li=list_init())==NULL)
{
perror (" Cannot allocate memory" ) ;
exit(-1);
}*/
}
My implementation of the list.c function list_init() is as follows:
list_t *list_init ()
{
list_t* newlist = malloc(sizeof(*newlist));
if (!newlist)
{
perror ("malloc-newlist");
exit (EXIT_FAILURE);
}`enter code here`
//newlist->first=NULL;
//newlist->last=NULL;
newlist->first = (struct list_elem *) malloc(sizeof(struct list_elem));
newlist->last = (struct list_elem *) malloc(sizeof(struct list_elem));
return newlist;
}
My list.h file is as follows:
struct list_elem {
struct list_elem *next; // Zeiger auf das naechste Element
char *data; // Zeiger auf ein Datenobject
};
typedef struct list {
struct list_elem *first;// erstes Element in der Liste
struct list_elem *last; // letztes Element in der Liste
} list_t;
/* function prototypes */
list_t *list_init ();
However, I do not know how I could change the implementation so that it does not occur anymore.
Thank you very much for your help.
While it is impossible to tell where your problem lies exactly, I suspect it lies in one of two places. One, you are initializing each data member with a string literal which is read-only on all but a very few systems. So if anywhere in your code you attempt to modify data you could expect a SegFault. The same would apply if you later attempt to free (pointer->data);
Two, you fail of assign your node->next pointers correctly leading to your traversal attempting to derefernce a NULL pointer or indeterminate pointer leading to the same SegFault. This can occur if your append function fails to handle the if (!list->first) { ... } case correctly or the else case where you will be required to set pointer->next = newnode;
There is really no way to tell unless you post a A Minimal, Complete, and Verifiable Example (MCVE), but given list operations are somewhat generic, you could correct the shortcomings with something similar to the following for your init() and append() functions (with an added print() and free() functions thrown in for good measure), e.g.
#include <stdio.h>
#include <stdlib.h>
struct list_elem {
struct list_elem *next; // Zeiger auf das naechste Element
char *data; // Zeiger auf ein Datenobject
};
typedef struct list {
struct list_elem *first;// erstes Element in der Liste
struct list_elem *last; // letztes Element in der Liste
} list_t;
/* function prototypes */
list_t *list_init ();
struct list_elem *list_append (list_t *list, char *data);
void list_print (list_t *list);
void list_free (list_t *list);
int main (void)
{
list_t *li = list_init();
if (list_append (li, (char[]){"erstes"}) == NULL ||
list_append (li, (char[]){"zweites"}) == NULL ||
list_append (li, (char[]){"drittes"}) == NULL) {
perror ("Cannot allocate memory" ) ;
exit (EXIT_FAILURE);
}
list_print (li);
list_free (li);
exit (EXIT_SUCCESS);
}
list_t *list_init (void)
{
list_t *newlist = malloc (sizeof *newlist);
if (!newlist) {
perror ("malloc-newlist");
exit (EXIT_FAILURE);
}
newlist->first = NULL;
newlist->last = NULL;
return newlist;
}
struct list_elem *list_append (list_t *list, char *data)
{
struct list_elem *node = NULL;
if (!list)
return NULL;
if (!(node = malloc (sizeof *node))) {
perror ("malloc-node");
return NULL;
}
node->data = data;
node->next = NULL;
if (!list->first)
list->first = node;
else {
struct list_elem *iter = list->first;
while (iter->next)
iter = iter->next;
iter->next = node;
}
return (list->last = node);
}
void list_print (list_t *list)
{
struct list_elem *iter = NULL;
if (!list)
return;
iter = list->first;
while (iter) {
printf ("%s\n", iter->data);
iter = iter->next;
}
}
void list_free (list_t *list)
{
struct list_elem *iter = NULL;
if (!list)
return;
iter = list->first;
while (iter) {
struct list_elem *victim = iter;
iter = iter->next;
free (victim);
}
free (list);
}
Example Use/Output
$ ./bin/ll_list_elem
erstes
zweites
drittes
Memory Use/Error Check
There is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?
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_list_elem
==22383== Memcheck, a memory error detector
==22383== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==22383== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==22383== Command: ./bin/ll_list_elem
==22383==
erstes
zweites
drittes
==22383==
==22383== HEAP SUMMARY:
==22383== in use at exit: 0 bytes in 0 blocks
==22383== total heap usage: 4 allocs, 4 frees, 64 bytes allocated
==22383==
==22383== All heap blocks were freed -- no leaks are possible
==22383==
==22383== For counts of detected and suppressed errors, rerun with: -v
==22383== 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.
Using last pointer with Forward-Chaining
Since you have a last pointer there is no need for a generic iteration of the list to find the last pointer. I suspect you are intending to use forward-chaining. In that case, you can simply modify append() as follows:
if (!list->first)
list->first = node;
else
list->last->next = node;
(note: the list->last = node assignment is handled in the return)
i think you are not allocate memory in proper way.
TList *list_init()
{
TList *newList = (TList *) malloc(sizeof(TList));
newList->first = (struct list_elem *) malloc(sizeof(structlist_elem));
newList->last = (struct list_elem *) malloc(sizeof(struct list_elem));
newList->first->next= NULL;
newList->last->next= NULL;
return newList;
}
Related
I'm trying to make a shop system. I have a linked list and a stack. The linked list store the Person information (id and name) and the Stack store the Products (product).
I need to link both of them but I don't know how. I want to create some Person and the person will choose some products like:
Person1:
Apple
Orange
Person2:
Strawberry
Lemon
Every person will have their chosen products.
Here's my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define TAM 30
typedef int ID;
typedef char PRODUCT[TAM];
typedef struct stack{
int nItems;
int capacity;
PRODUCT *product;
} Stack;
typedef struct person{
Stack *stack;
ID id;
char name[TAM];
struct person *next;
}Person;
typedef struct list{
Person *head;
Person *end;
}List;
void createList(List *list){
list->head = NULL;
list->end = NULL;
}
void insert(List *list, Person person){
Person *newPerson = (Person*)malloc(sizeof(Person));
Person *ant = NULL;
Person *a = list->head;
strcpy(newPerson->name, person.name);
newPerson->id = person.id;
while(a != NULL && a->id < a->id){
ant = a;
a = a->next;
}
if (ant == NULL){
newPerson->next = list->head;
list->head = newPerson;
}else{
newPerson->next = ant->next;
ant->next = newPerson;
}
}
void printList(List *list){
Person *a;
a = list->head;
while(a != NULL){
printf("ID: %d | Name: %s\n", a->id, a->name);
a = a->next;
}
}
Stack *createStack(int capacity){
Stack *stack = malloc(sizeof(Stack));
stack->product = malloc(sizeof(int) * capacity);
stack->capacity = capacity;
return stack;
};
bool push(Stack *stack, PRODUCT product){
if (stack->capacity == stack->nItems){
return false;
}
strcpy(stack->product[stack->nItems], product);
return true;
}
void printStack(Stack *stack){
int i;
for(i = 0; i < stack->nItems; i++){
printf("product: %s\n", stack->product[stack->nItems - i - 1]);
}
}
int main(){
int capacity = 10;
Stack *stack = createStack(10);
List list;
Person p1 = {1, "James"};
PRODUCT prod1 = {"Apple"};
createList(&list);
insert(&list, p1);
push(stack, prod1);
printList(&list);
printStack(stack);
return 0;
}
You can do exactly what you are attempting to do, but you have missed the point of Person including Stack *stack; by creating a separate Person and Stack in main(). There is no relationship between the two if you create a separate Person and a separate Stack. Because Person includes a pointer to stack, you must create the stack when you insert() the person.
You are thinking along the correct lines in how you approach the list and stack, you are just struggling to get the pieces put together correctly and in the right order.
Let's start with your attempt in main() to create p1 which you want to use as a temporary struct to insert. You declare and initialize Person p1 = {1, "James"};, but the first member in Person is Stack *stack;. So, of course, the compiler chokes when it sees 1 as the initializer for Stack *stack;. You can make your wanted initialization work by moving Stack *stack; to the 3rd member in the struct, e.g.
typedef struct person {
ID id;
char name[TAM];
Stack *stack; /* move to 3rd member */
struct person *next;
} Person;
When you allocate memory, you must validate EVERY allocation. If there are multiple allocations in any one function, if the 2nd (or later) allocation fails, you must clean up (free()) the prior allocations before returning an error condition. You can see that in createStack(), e.g.
Stack *createStack (int capacity)
{
Stack *stack = malloc (sizeof *stack);
if (!stack) { /* validate EVERY allocation */
perror ("malloc-stack");
return NULL;
}
stack->product = malloc (capacity * sizeof *stack->product);
if (!stack->product) { /* validate EVERY allocation */
perror ("malloc-stack->product");
free (stack); /* free stack memory on failure */
return NULL;
}
stack->capacity = capacity;
stack->nItems = 0; /* don't forget to initialize nItems 0 */
return stack;
}
As mentioned above, since the Stack *stack; is a member of Person, you must create the stack as part of creating the Person. (you can do it in separate functions if you like, but you create Person->stack NOT some separate unrelated stack). It is common to do it in the same function where you create your Person, e.g.
Person *insert (List *list, const Person *person, int capacity)
{
Person *newPerson = malloc (sizeof *newPerson);
if (!newPerson) { /* validate EVERY allocation */
perror ("malloc-newPerson");
return NULL;
}
*newPerson = *person; /* assign automatic struct members */
/* create / validate stack */
newPerson->stack = createStack (capacity);
if (!newPerson->stack) {
free (newPerson); /* free newPerson memory on failure */
return NULL;
}
newPerson->next = NULL; /* initialize pointers NULL */
if (!list->head) { /* good job on the end ptr, now use it */
list->head = list->end = newPerson;
}
else {
list->end->next = newPerson;
list->end = newPerson;
}
return newPerson;
}
(NOTE: insert cannot be type void. You must return a type that indicates whether your allocation within the function succeeded or failed. Simply returning the pointer you create in the function (or NULL on failure) works fine.)
Your push() function should provide a meaningful error if the stack is full, e.g.
bool push (Stack *stack, const PRODUCT product)
{
if (stack->capacity == stack->nItems) {
/* provide meaningful error messages */
fprintf (stderr, "error: failed to add: %s (stack full)\n",
product);
return false;
}
strcpy (stack->product[stack->nItems], product);
stack->nItems++; /* don't forget to increment nItems */
return true;
}
(note: the addition of the increment of stack->nItems, without it you just keep overwriting the first product...)
Since your stack is part of Person, you need to printStack() as part of printList(), e.g.
void printStack (Stack *stack)
{
int i;
if (!stack->nItems) { /* don't forget to handle empty-stack */
puts ("stack-empty"); /* or your printf() ivokes UB */
return;
}
for (i = 0; i < stack->nItems; i++) {
printf (" product: %s\n", stack->product[stack->nItems - i - 1]);
}
}
void printList (List *list){
Person *a = list->head;
while (a != NULL) {
printf ("\nID: %d | Name: %s\n", a->id, a->name);
printStack (a->stack);
a = a->next;
}
}
(note: the addition of the validation in printStack() to avoid Undefined Behavior by referencing a negative index if the stack is empty)
(also note: there is no reason to print the stack in reverse order -- unless you just want to. You are adding the product at stack->nItems each time so they will be in the order added)
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. You do fine with (1), but completely fail doing (2).
Yes, since your list is created in main(), there is no technical memory-leak since the memory will be freed on program exit, but you will not always be creating lists in main(). In that case, if you fail to free() before leaving the function you created in -> memory-leak. Build good habits early, always track and free what you allocate. A simple routine to free your stack and Person nodes in your list could be:
void del_stack (Stack *stack)
{
free (stack->product);
free (stack);
}
void del_list (List *list)
{
Person *person = list->head;
while (person) {
Person *victim = person;
del_stack (victim->stack);
person = person->next;
free (victim);
}
}
Now you can simply call del_list (&list); to free all allocated memory.
Lastly, be very careful trying to be too creative with typedef. Never typedef pointers and make sure you understand a typedef of arrays. In your code you have:
typedef char PRODUCT[TAM];
and then in Stack you have:
PRODUCT *product;
What is product? Is it a pointer-to-array or is it an array-of-pointers? (are you sure?) (hint: you have single-allocation, single-free, so it can only be one of the two)
Now in main(), it is fine to use a temporary pointer to Person to initialize and then insert in your list. But don't confuse the temporary Person with the node created in the list. Putting all the above together, and intentionally attempting to insert 12 products where only 10 will fit to exercise the checks in the code, you could do:
int main() {
int capacity = 10;
List list;
PRODUCT prod = "";
createList (&list);
Person tmp = { .id = 1, .name = "James"};
/* must create stack with person */
Person *p1 = insert (&list, &tmp, capacity);
if (!p1) { /* validate node & stack creation succeeds */
return 1;
}
for (int i = 0; i < capacity + 2; i++) {
sprintf (prod, "Product_%d", i + 1);
if (!push (p1->stack, prod)) {
break;
}
}
printList (&list);
del_list (&list); /* don't forget to free what you allocate */
}
Example Use/Output
$ ./bin/llstack
error: failed to add: Product_11 (stack full)
ID: 1 | Name: James
product: Product_10
product: Product_9
product: Product_8
product: Product_7
product: Product_6
product: Product_5
product: Product_4
product: Product_3
product: Product_2
product: Product_1
Memory Use/Error Check
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/llstack
==7287== Memcheck, a memory error detector
==7287== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7287== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==7287== Command: ./bin/llstack
==7287==
error: failed to add: Product_11 (stack full)
ID: 1 | Name: James
product: Product_10
product: Product_9
product: Product_8
product: Product_7
product: Product_6
product: Product_5
product: Product_4
product: Product_3
product: Product_2
product: Product_1
==7287==
==7287== HEAP SUMMARY:
==7287== in use at exit: 0 bytes in 0 blocks
==7287== total heap usage: 4 allocs, 4 frees, 1,396 bytes allocated
==7287==
==7287== All heap blocks were freed -- no leaks are possible
==7287==
==7287== For lists of detected and suppressed errors, rerun with: -s
==7287== 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.
Complete Example Code
For easy reference, here are the complete modifications to your code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define TAM 30
typedef int ID;
typedef char PRODUCT[TAM];
typedef struct stack {
int nItems;
int capacity;
PRODUCT *product;
} Stack;
typedef struct person {
ID id;
char name[TAM];
Stack *stack; /* move to 3rd member */
struct person *next;
} Person;
typedef struct list {
Person *head;
Person *end;
} List;
void createList (List *list)
{
list->head = NULL;
list->end = NULL;
}
Stack *createStack (int capacity)
{
Stack *stack = malloc (sizeof *stack);
if (!stack) { /* validate EVERY allocation */
perror ("malloc-stack");
return NULL;
}
stack->product = malloc (capacity * sizeof *stack->product);
if (!stack->product) { /* validate EVERY allocation */
perror ("malloc-stack->product");
free (stack); /* free stack memory on failure */
return NULL;
}
stack->capacity = capacity;
stack->nItems = 0; /* don't forget to initialize nItems 0 */
return stack;
}
Person *insert (List *list, const Person *person, int capacity)
{
Person *newPerson = malloc (sizeof *newPerson);
if (!newPerson) { /* validate EVERY allocation */
perror ("malloc-newPerson");
return NULL;
}
*newPerson = *person; /* assign automatic struct members */
/* create / validate stack */
newPerson->stack = createStack (capacity);
if (!newPerson->stack) {
free (newPerson); /* free newPerson memory on failure */
return NULL;
}
newPerson->next = NULL; /* initialize pointers NULL */
if (!list->head) {
list->head = list->end = newPerson;
}
else {
list->end->next = newPerson;
list->end = newPerson;
}
return newPerson;
}
bool push (Stack *stack, const PRODUCT product)
{
if (stack->capacity == stack->nItems) {
/* provide meaningful error messages */
fprintf (stderr, "error: failed to add: %s (stack full)\n",
product);
return false;
}
strcpy (stack->product[stack->nItems], product);
stack->nItems++; /* don't forget to increment nItems */
return true;
}
void printStack (Stack *stack)
{
int i;
if (!stack->nItems) { /* don't forget to handle empty-stack */
puts ("stack-empty"); /* or your printf() ivokes UB */
return;
}
for (i = 0; i < stack->nItems; i++) {
printf (" product: %s\n", stack->product[stack->nItems - i - 1]);
}
}
void printList (List *list){
Person *a = list->head;
while (a != NULL) {
printf ("\nID: %d | Name: %s\n", a->id, a->name);
printStack (a->stack);
a = a->next;
}
}
void del_stack (Stack *stack)
{
free (stack->product);
free (stack);
}
void del_list (List *list)
{
Person *person = list->head;
while (person) {
Person *victim = person;
del_stack (victim->stack);
person = person->next;
free (victim);
}
}
int main() {
int capacity = 10;
List list;
PRODUCT prod = "";
createList (&list);
Person tmp = { .id = 1, .name = "James"};
/* must create stack with person */
Person *p1 = insert (&list, &tmp, capacity);
if (!p1) { /* validate node & stack creation succeeds */
return 1;
}
for (int i = 0; i < capacity + 2; i++) {
sprintf (prod, "Product_%d", i + 1);
if (!push (p1->stack, prod)) {
break;
}
}
printList (&list);
del_list (&list); /* don't forget to free what you allocate */
}
Let me know if you have further questions.
I defined a display function to display the content of a queue:
void display(queue_t* s) {
queue_t* c = s;
int i = c->size;
while (i > 0) {
const char* elem = dequeue(c).value;
printf("Item n°%d : %s\n",i,elem);
i--;
};
};
where queue_t is defined as follow:
typedef struct queue {
node_t* head;
node_t* tail;
int size;
} queue_t;
and dequeue is a function that removes a node from the queue and frees it. This function works as intended.
The function display should display the content of the queue without deleting its content but when testing it, the queue is empty if I call display before removing each element one by one by hand by calling dequeue. I thought that queue_t* c = s; would copy the element without any link between c and s.
How can I copy the content of s into c without any link between the two variables ?
EDIT - MWE
Header file - queue.h
#ifndef QUEUE_H
#define QUEUE_H
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
typedef struct element {
bool type;
const char* value;
} element_t;
typedef struct node {
element_t content;
struct node* previous;
struct node* next;
} node_t;
typedef struct queue {
node_t* head;
node_t* tail;
int size;
} queue_t;
queue_t* init_queue(void);
node_t* init_node(element_t e);
void queue(queue_t* s, element_t e);
element_t dequeue(queue_t* s);
void display(queue_t* s);
#endif
Source code - queue.c
#include "queue.h"
queue_t* init_queue(void) {
queue_t* new = (queue_t*)malloc(sizeof(queue_t));
new->head = NULL;
new->tail = NULL;
new->size = 0;
return new;
};
node_t* init_node(element_t e) {
node_t* new = (node_t*)malloc(sizeof(node_t));
new->content = e;
new->next = NULL;
new->previous = NULL;
return new;
};
void queue(queue_t* s, element_t e) {
node_t* n = init_node(e);
if (s->size == 0) {
s->head = n;
s->tail = n;
s->size = 1;
} else {
n->previous = s->tail;
s->tail = n;
s->size++;
};
};
element_t dequeue(queue_t* s) {
if (s->size == 0) {
element_t empty;
empty.type = true;
empty.value = "0";
return empty;
} if (s->size == 1) {
element_t c = s->head->content;
node_t* old = s->head;
s->head = NULL;
s->size = 0;
s->tail = NULL;
free(old);
return c;
} else {
element_t c = s->tail->content;
node_t* old = s->tail;
s->tail = s->tail->previous;
s->tail->next = NULL;
s->size--;
free(old);
return c;
};
};
void display(queue_t* s) {
queue_t* c = s;
int i = c->size;
while (i > 0) {
const char* elem = dequeue(c).value;
printf("Item n°%d : %s\n",i,elem);
i--;
};
};
Test file - test.c
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include "queue.h"
int main(void) {
element_t e1 = {.type = false, .value = "1"};
element_t e2 = {.type = false, .value = "5"};
element_t e3 = {.type = false, .value = "10"};
queue_t* test = init_queue();
queue(test,e1);
queue(test,e2);
queue(test,e3);
display(test);
element_t e4 = dequeue(test);
printf("%s\n",e4.value);
element_t e5 = dequeue(test);
printf("%s\n",e5.value);
element_t e6 = dequeue(test);
printf("%s\n",e6.value);
element_t e7 = dequeue(test);
printf("%s\n",e7.value);
return 0;
}
Run the test file using
gcc -g -std=c99 -Wall -o test.o -c test.c
gcc -g -std=c99 -Wall -o queue.o -c queue.c
gcc -g -std=c99 -Wall -o test queue.o test.o
A few issues ...
You're leaking memory.
Passing structs by value doesn't scale.
queue does not set next for s->tail so forward list traversal doesn't work.
queue is much more complicated than it needs to be
dequeue should be split into two functions: dequeue and destroy
display should just display a list by traversal.
init_node should do a deep copy of content.value
You're leaking memory.
dequeue doesn't free old->value.
It returns a copy that has a valid value.
But, in display, you do:
const char *elem = dequeue(c).value;
You never free elem
Here is the valgrind output:
==2168790== Memcheck, a memory error detector
==2168790== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2168790== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2168790== Command: ./fix1
==2168790==
Item n°3 : 10
Item n°2 : 5
Item n°1 : 1
0
0
0
0
==2168790==
==2168790== HEAP SUMMARY:
==2168790== in use at exit: 24 bytes in 1 blocks
==2168790== total heap usage: 5 allocs, 4 frees, 4,216 bytes allocated
==2168790==
==2168790== LEAK SUMMARY:
==2168790== definitely lost: 24 bytes in 1 blocks
==2168790== indirectly lost: 0 bytes in 0 blocks
==2168790== possibly lost: 0 bytes in 0 blocks
==2168790== still reachable: 0 bytes in 0 blocks
==2168790== suppressed: 0 bytes in 0 blocks
==2168790== Rerun with --leak-check=full to see details of leaked memory
==2168790==
==2168790== For lists of detected and suppressed errors, rerun with: -s
==2168790== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
dequeue returns "by value" -- this doesn't scale
Add int data[10000000]; to element_t and watch the stack blow up ;-)
In general, passing around a struct by value has similar issues in other places.
dequeue should just dequeue the element.
Do one thing well. dequeue does a dequeue and a [partial/incomplete] destroy.
We should have a separate function destroy that fully frees the node_t/element_t
And, dequeue [as written] should be renamed as pop or dequeue_back because it works from tail to head.
Also, we'd probably like a dequeue_front that works from head to tail
And, it's much more useful if the functions return pointers to the dequeued elements and let the caller use destroy on them when the caller is finished with them.
display should just display the queue and not alter/destroy it.
It could be split [after fixup] into two functions (e.g.) display_reverse and display_forward
Here is the refactored code. It is annotated:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#ifndef DEEPCOPY
#define DEEPCOPY 1
#endif
typedef struct element {
bool type;
#if DEEPCOPY
char *value;
#else
const char *value;
#endif
} element_t;
typedef struct node {
element_t content;
struct node *previous;
struct node *next;
} node_t;
typedef struct queue {
node_t *head;
node_t *tail;
int size;
} queue_t;
queue_t *init_queue(void);
node_t *init_node(const element_t *e);
void queue(queue_t *s, const element_t *e);
node_t *dequeue_back(queue_t *s);
void display_forward(const queue_t *s);
void display_reverse(const queue_t *s);
// prevent/reduce unintential "use after free"
#define FREE(_ptr) \
do { \
free(_ptr); \
_ptr = NULL; \
} while (0)
queue_t *
init_queue(void)
{
queue_t *new = malloc(sizeof(queue_t));
new->head = NULL;
new->tail = NULL;
new->size = 0;
return new;
}
node_t *
init_node(const element_t *e)
{
node_t *new = malloc(sizeof(node_t));
new->content = *e;
// NOTE/FIX: we should deep copy the element
#if DEEPCOPY
new->content.value = strdup(e->value);
#endif
new->next = NULL;
new->previous = NULL;
return new;
}
void
queue(queue_t *s, const element_t *e)
{
node_t *n = init_node(e);
if (s->size == 0) {
s->head = n;
s->tail = n;
s->size = 1;
}
else {
n->previous = s->tail;
// NOTE/BUG: without this forward traversal doesn't work
#if 1
s->tail->next = n;
#endif
s->tail = n;
s->size++;
}
}
void
destroy_element(element_t *elem)
{
#if DEEPCOPY
FREE(elem->value);
#endif
}
node_t *
destroy_node(node_t *node)
{
destroy_element(&node->content);
FREE(node);
// convenience to caller
return node;
}
node_t *
dequeue_back(queue_t *s)
{
node_t *ret = s->tail;
if (ret != NULL) {
s->tail = ret->previous;
if (s->head == ret)
s->head = ret->next;
s->size -= 1;
}
return ret;
}
void
display_forward(const queue_t *s)
{
int i = 0;
const node_t *node = s->head;
for (; node != NULL; node = node->next) {
printf("Item n°%d : %s\n", i, node->content.value);
i++;
}
}
void
display_reverse(const queue_t *s)
{
int i = s->size;
const node_t *node = s->tail;
for (; node != NULL; node = node->previous) {
printf("Item n°%d : %s\n", i, node->content.value);
i--;
}
}
int
main(void)
{
element_t e1 = {.type = false,.value = "1" };
element_t e2 = {.type = false,.value = "5" };
element_t e3 = {.type = false,.value = "10" };
queue_t *test = init_queue();
queue(test, &e1);
queue(test, &e2);
queue(test, &e3);
printf("display_reverse:\n");
display_reverse(test);
printf("display_forward:\n");
display_forward(test);
for (int i = 4; i <= 7; ++i) {
node_t *node = dequeue_back(test);
if (node == NULL)
break;
printf("main: %s (dequeue_back)\n", node->content.value);
destroy_node(node);
}
#if 1
FREE(test);
#endif
return 0;
}
In the above code, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k
Here is the program output:
display_reverse:
Item n°3 : 10
Item n°2 : 5
Item n°1 : 1
display_forward:
Item n°0 : 1
Item n°1 : 5
Item n°2 : 10
main: 10 (dequeue_back)
main: 5 (dequeue_back)
main: 1 (dequeue_back)
I thought that queue_t* c = s; would copy the element without any link between c and s.
No. It only makes a copy of the pointer s that you passed in the function.
Copying would require you to define "copy semantics", in other words:
What happens if you copy a queue, will the "elements" in your queue be copied too? (This is trivial for primitive types, but not so easy for objects).
If your elements are objects, what copy semantic do they have?
(in the case of strings, you'll need something like strncpy or memcpy to copy all characters into a new buffer)
Also, making a "true" (truly independent) copy you will need to make a complete copy of the entire queue.
This means setting up a completely new queue with all needed element data copied.
I'm phrasing it this way, because you certainly cannot re-use the head, tail, next and previous pointers from your existing queue, otherwise it would not be independent.
And... it's a bad design choice to have a method that is supposedly to be read-only* to modify your queue (data).
To display the contents of your queue you don't need to modify it.
You need to be aware, especially in C, when, how, by whom and for how long your data structures are being accessed. Just a single dangling pointer makes your whole program unpredictable.
*displaying data is considered a read-only operation by most (if not all) programmers.
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 have written a code in C language to implement a stack using LinkedList algorithm. Here is the code........
#include<stdio.h>
#include<stdlib.h>
#include<limits.h>
struct listNode {
int data;
struct listNode *next;
};
struct stack{
struct stack *top;
};
struct stack *createstk(){
struct stack *stk;
stk=malloc(sizeof(struct stack));
stk->top=NULL;
return stk;
}
void push(struct stack *stk,int data){
struct listNode *temp;
temp=malloc(sizeof(struct listNode));
if(!temp){
printf("heap overflow");
return;
}
temp->data=data;
temp->next=stk->top;
stk->top=temp;
}
int pop(struct stack *stk){
if(isEmpty(stk))
return INT_MIN;
int data;
struct listNode *temp;
temp= stk->top;
stk->top=stk->top->next;
data=temp->data;
delete(temp);
return data;
}
int peek(struct stack *stk){
if(isEmpty(stk))
return INT_MIN;
return stk->top->data;
}
int isEmpty(struct stack *stk){
return stk->top==NULL;
}
void deleteStack(struct stack *stk){
struct listNode *temp,*p;
p=stk->top;
while(p){
temp=p->next;
p=p->next;
free(temp);
}
free(stk);
}
int main(){
int i=0;
struct stack *stk=createstk();
for(i=0;i<=10;i++)
push(stk,i);
printf("Top Element is %d",peek(stk));
for(i=0;i<=10;i++){
printf("popped element is %d",pop(stk));
}
if(isEmpty(stk))
printf("stack is empty");
else
printf("stack is not empty");
deleteStack(stk);
return 0;
}
[warning]assignment from incompatible pointer type.
As you can see above in the picture. I am a newbie in coding world and facing this error first time. That's why I don't know what to do. Please tell me...
The field top in the type stack has the wrong type. Change
struct stack {
struct stack *top;
};
to
struct stack {
struct listNode *top;
};
Your have a large number of error beginning with the incorrect type for stack->top, as correctly noted by August Karlstrom your type for the member top must be struct listNode *top;, e.g.
struct stack {
struct listNode *top;
};
Following the correction, you must either reorder your functions or provide function prototypes for isEmpty() and deleteStack(). Moving them up in your code before before their first use will solve the problem, e.g.
struct stack *createstk()
{
struct stack *stk;
stk = malloc (sizeof (struct stack));
stk->top = NULL;
return stk;
}
int isEmpty (struct stack *stk)
{
return stk->top == NULL;
}
void deleteStack (struct stack *stk)
{
struct listNode *temp, *p;
p = stk->top;
while (p) {
temp = p->next;
p = p->next;
free (temp);
}
free (stk);
}
...
Next, this is C, not C++ there is no delete function to free memory, so in pop(), you must call free (temp); instead of delete (temp);.
Finally, provide spacing to make your code readable. Incorporating the above, you could do the following:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
struct listNode {
int data;
struct listNode *next;
};
struct stack {
struct listNode *top;
};
struct stack *createstk()
{
struct stack *stk;
stk = malloc (sizeof (struct stack));
stk->top = NULL;
return stk;
}
int isEmpty (struct stack *stk)
{
return stk->top == NULL;
}
void deleteStack (struct stack *stk)
{
struct listNode *temp, *p;
p = stk->top;
while (p) {
temp = p->next;
p = p->next;
free (temp);
}
free (stk);
}
void push (struct stack *stk, int data){
struct listNode *temp;
temp = malloc (sizeof *temp);
if (!temp) {
perror ("push()-malloc-temp");
return;
}
temp->data = data;
temp->next = stk->top;
stk->top = temp;
}
int pop(struct stack *stk){
int data;
struct listNode *temp;
if (isEmpty(stk))
return INT_MIN;
temp = stk->top;
data = temp->data;
stk->top = stk->top->next;
free (temp);
return data;
}
int peek(struct stack *stk){
if(isEmpty(stk))
return INT_MIN;
return stk->top->data;
}
int main (void) {
int i=0;
struct stack *stk=createstk();
for (i = 0; i <= 10; i++)
push(stk,i);
printf("Top Element is %d\n",peek(stk));
for (i = 0; i <= 10; i++)
printf (" popped element is %d\n",pop(stk));
if (isEmpty(stk))
printf ("stack is empty\n");
else
printf ("stack is not empty\n");
deleteStack(stk);
return 0;
}
Example Use/Output
$ ./bin/stack_ll
Top Element is 10
popped element is 10
popped element is 9
popped element is 8
popped element is 7
popped element is 6
popped element is 5
popped element is 4
popped element is 3
popped element is 2
popped element is 1
popped element is 0
stack is empty
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/stack_ll
==25935== Memcheck, a memory error detector
==25935== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==25935== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==25935== Command: ./bin/stack_ll
==25935==
Top Element is 10
popped element is 10
popped element is 9
popped element is 8
popped element is 7
popped element is 6
popped element is 5
popped element is 4
popped element is 3
popped element is 2
popped element is 1
popped element is 0
stack is empty
==25935==
==25935== HEAP SUMMARY:
==25935== in use at exit: 0 bytes in 0 blocks
==25935== total heap usage: 12 allocs, 12 frees, 184 bytes allocated
==25935==
==25935== All heap blocks were freed -- no leaks are possible
==25935==
==25935== For counts of detected and suppressed errors, rerun with: -v
==25935== 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.
If you have any further questions, just let me know.
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)