Printing just some elements of a List using pointers - c

Create a function that receives two Lists (L1,L2), initialize L2, and insert in L2 the elements of L1 excluding the ones in odd postion (Assume that the first element of the list is in position zero). Then print L2.
My prof gave us the solution (below), but I need to create a main() that calls this function and returns L2, and since I'm a newbie nothing seems to work.
I tried initializing L1 and then calling the function, but all I got was a huge amount of errors.
That's the final function:
struct list{
int value;
struct list * nextPtr;
};
void createSubList(struct list * l1Ptr, struct list ** l2PtrPtr) {
init(l2PtrPtr);
while(l1Ptr!=NULL) {
pre_insert(l2PtrPtr, l1Ptr­>value);
l1Ptr = l1Ptr­>nextPtr;
if (l1Ptr != NULL)
l1Ptr = l1Ptr­>nextPtr;
}
}
I expect to see L2 printed after calling the function.
That's my final file:
#include <stdlib.h>
#include <string.h>
struct list {
int value;
struct list *nextPtr;
};
void init( struct list **ptrptr){
*ptrptr!=NULL;
}
void prn (struct list * lptr) {
while (lptr) {
printf (" %d", lptr->value);
lptr = lptr->nextPtr;
}
putchar ('\n'); }
void pre_insert(struct list ** ptrptr, int value){
struct list * tmp_ptr;
tmp_ptr=*ptrptr;
*ptrptr=(struct list *)malloc(sizeof(struct list));
(*ptrptr)->value=value;
(*ptrptr)->nextPtr=tmp_ptr;
}
void createSubList(struct list* l1Ptr, struct list** l2PtrPtr) {
init(l2PtrPtr);
while(l1Ptr!=NULL) {
pre_insert(l2PtrPtr, l1Ptr->value);
l1Ptr = l1Ptr->nextPtr;
if (l1Ptr != NULL)
l1Ptr = l1Ptr->nextPtr;
}
prn(l1Ptr);
}
void main(){
struct list* l1Ptr;
init(&l1Ptr);
struct list* l2ptr;
init(&l2ptr);
pre_insert(&l1Ptr , 1);
pre_insert(&l1Ptr , 2);
pre_insert(&l1Ptr , 3);
pre_insert(&l1Ptr , 4);
pre_insert(&l1Ptr , 5);
pre_insert(&l1Ptr , 6);
createSubList(l1Ptr,&l2ptr);
}
Errors I get:
[Finished in 0.1s with exit code -11]
[shell_cmd: gcc "/home/vittorio/Scrivania/CProjects/new.c" -o "/home/vittorio/Scrivania/CProjects/new" && "/home/vittorio/Scrivania/CProjects/new"]
[dir: /home/vittorio/Scrivania/CProjects]
[path: /home/vittorio/bin:/home/vittorio/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin]

Once you get the non-ASCII chars squared away. Take your implementation piece-by-piece. For starters, unless you are on a non-conforming system, the proper declarations for main are int main (void) and int main (int argc, char **argv) (which you will see written with the equivalent char *argv[]). note: main is a function of type int and it returns a value. See: C11 Standard §5.1.2.2.1 Program startup p1 (draft n1570). See also: What should main() return in C and C++?.
So, for conforming implementation, main() without arguments should be:
int main (void) {
struct list *l1Ptr = NULL;
struct list *l2ptr = NULL;
...
(note: just ditch your init() function, you don't need to function call overhead just to set a pointer NULL)
Next issue along the way is your pre_insert must distinguish between adding the 1st node to the list, and adding all others. For the 1st node, just set your *ptrptr to your initialize tmp_ptr to establish the head of the list. For the remaining nodes, you are using chaining where you set tmp_ptr->nextPtr = *ptrptr; to make the next pointer in the new node point to the old-start of your list, then set *ptrptr = tmp_ptr; to make it your new start of the list, e.g.
void pre_insert (struct list **ptrptr, int value)
{
struct list *tmp_ptr = malloc (sizeof *tmp_ptr); /* don't cast malloc */
if (tmp_ptr == NULL) { /* validate EVERY allocation */
perror ("malloc-tmp_ptr");
exit (EXIT_FAILURE);
}
tmp_ptr->value = value; /* initialize struct members */
tmp_ptr->nextPtr = NULL;
if (!*ptrptr) /* if 1st node, simply assign */
*ptrptr = tmp_ptr;
else {
tmp_ptr->nextPtr = *ptrptr; /* otherwise, set tmp->next to 1st */
*ptrptr = tmp_ptr; /* now set list to point to tmp */
}
}
Your createSubList had similar redundant logic, showing your were struggling. All you need is a simply 1/0 toggle to add or skip nodes from list1. For example:
void createSubList (struct list *l1Ptr, struct list **l2PtrPtr)
{
int i = 0;
while (l1Ptr != NULL) {
if (i == 0) { /* only store even nodes */
pre_insert (l2PtrPtr, l1Ptr->value);
i = 1;
}
else
i = 0;
l1Ptr = l1Ptr->nextPtr;
}
}
As discussed in the comments, you need a way to print your lists, and equally important, a way to free the memory allocated to the nodes where you are done with them. Simple functions are all you need, e.g.
void prnlist (struct list *lptr)
{
while (lptr) {
printf (" %d", lptr->value);
lptr = lptr->nextPtr;
}
putchar ('\n');
}
void freelist (struct list *lptr)
{
while (lptr) {
struct list *victim = lptr;
lptr = lptr->nextPtr;
free (victim);
}
}
(note: do you see why you have to save a pointer to the current node, and then advance the node before calling free on your victim?)
That's it, aside from my additional comments in-line. Putting it altogether you could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list {
int value;
struct list *nextPtr;
};
void pre_insert (struct list **ptrptr, int value)
{
struct list *tmp_ptr = malloc (sizeof *tmp_ptr); /* don't cast malloc */
if (tmp_ptr == NULL) { /* validate EVERY allocation */
perror ("malloc-tmp_ptr");
exit (EXIT_FAILURE);
}
tmp_ptr->value = value; /* initialize struct members */
tmp_ptr->nextPtr = NULL;
if (!*ptrptr) /* if 1st node, simply assign */
*ptrptr = tmp_ptr;
else {
tmp_ptr->nextPtr = *ptrptr; /* otherwise, set tmp->next to 1st */
*ptrptr = tmp_ptr; /* now set list to point to tmp */
}
}
void createSubList (struct list *l1Ptr, struct list **l2PtrPtr)
{
int i = 0;
while (l1Ptr != NULL) {
if (i == 0) { /* only store even nodes */
pre_insert (l2PtrPtr, l1Ptr->value);
i = 1;
}
else
i = 0;
l1Ptr = l1Ptr->nextPtr;
}
}
void prnlist (struct list *lptr)
{
while (lptr) {
printf (" %d", lptr->value);
lptr = lptr->nextPtr;
}
putchar ('\n');
}
void freelist (struct list *lptr)
{
while (lptr) {
struct list *victim = lptr;
lptr = lptr->nextPtr;
free (victim);
}
}
int main (void) {
struct list *l1Ptr = NULL;
struct list *l2ptr = NULL;
for (int i = 1; i < 10; i++)
pre_insert (&l1Ptr , i);
createSubList (l1Ptr, &l2ptr);
prnlist (l2ptr); /* print list 2 */
freelist (l1Ptr); /* don't forget to free what you allocate */
freelist (l2ptr);
}
Example Use/Output
$ ./bin/llcreatesublist
1 3 5 7 9
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/llcreatesublist
==23324== Memcheck, a memory error detector
==23324== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23324== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==23324== Command: ./bin/llcreatesublist
==23324==
1 3 5 7 9
==23324==
==23324== HEAP SUMMARY:
==23324== in use at exit: 0 bytes in 0 blocks
==23324== total heap usage: 14 allocs, 14 frees, 224 bytes allocated
==23324==
==23324== All heap blocks were freed -- no leaks are possible
==23324==
==23324== For counts of detected and suppressed errors, rerun with: -v
==23324== 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.

You need to add #include <stdio.h>
In the init function the code *ptrptr!=NULL; shall be *ptrptr=NULL;, i.e. no !
Then change void main(){ to int main(){
That should solve the warnings.
Then you need to do the actual printing. So in main do:
prn(l1Ptr);
createSubList(l1Ptr,&l2ptr);
prn(l2ptr);
BTW: Notice that the prn(l1Ptr); inside createSubList will not print anything as l1Ptr is already NULL when you call prn. So you should simply delete that line.
With the above change your output should be:
6 5 4 3 2 1
2 4 6
The created sub-list is reverted compared to the original list due to use of pre_insert that adds new nodes in the front.

Related

Linked List doesn't take in the next element (C language)

I am new to C language and I am working on a Linked List example.
The initialize() function seems to work fine, but after the first call of insert() the program crashes.
I think the problem comes from when a new element is added to the linked list, as if it overflows or it doesn't accept the new first element of the list.
I worked on a similar example with a linked list that has only an int element and it worked fine.
The code is as following :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Element Element;
struct Element
{
char f_name[10];
char l_name[10];
float score;
Element* next;
};
typedef struct Liste Liste;
struct Liste
{
Element* first;
};
Element* read()
{
Element* element = (Element*) malloc(sizeof(element));
if(element==NULL)
exit(EXIT_FAILURE);
printf("Please provide first name : \n");
scanf(" %s", element->f_name);
printf("Please provide last name : \n");
scanf(" %s", element->l_name);
printf("Please provide score : \n");
scanf("%f", &(element->score));
return element;
}
Liste* initialize()
{
Liste* liste = (Liste*) malloc(sizeof(liste));
Element* element = (Element*) malloc(sizeof(element));
if(liste==NULL || element==NULL)
exit(EXIT_FAILURE);
element = read();
element->next = NULL;
liste->first = element;
return liste;
}
void insert(Liste* liste)
{
Element* nouveau = (Element*) malloc(sizeof(nouveau));
if(liste==NULL || nouveau==NULL)
exit(EXIT_FAILURE);
nouveau = read();
nouveau->next = liste->first;
liste->first = nouveau;
}
int main()
{
Liste* maListe = (Liste*) malloc(sizeof(maListe));
maListe = initialize();
insert(maListe);
insert(maListe);
insert(maListe);
return 0;
}
What did I do wrong in this one ? and how should I fix it ?
Thanks.
I think the problem in your case is that you have written sizeof(element) where you need to have sizeof(Element). You have that in two different places.
Note that "element" is a variable of pointer type, so it has the size of a pointer (8 bytes probably) while "Element" is your struct type that has a much larger size. So, when you allocate only sizeof(element) bytes that is too small.
This kind of errors are easily found by running your program through valgrind.
While you already have an answer for your SegFault, there are additional areas where you can clean and refactor your code to work much more efficiently together. Since you are using a list structure liste to hold the pointer to the beginning the list in first, you may as well add another pointer last to point to the final node in the list and eliminate having to iterate to the last node on each insertion. With a last (or tail) pointer, your new node is always inserted at last->next. For instance you Liste struct could be:
typedef struct Liste Liste;
struct Liste {
Element *first, *last;
};
Your list functions should do one thing each, meaning initialize() should just allocate for and initialize the Liste node and its pointers. read() should allocate and read and return a valid pointer to a filled node, or NULL on failure. insert() should do just that, take the Liste list addressm and the node from read() and insert it into the list. Putting those functions together you could do:
Element *read()
{
Element *element = malloc (sizeof(*element)); /* allocate */
if (element == NULL) /* validate */
return NULL;
element->next = NULL; /* initialize */
printf ("\nPlease provide first name : ");
if (scanf ("%9s", element->f_name) != 1) /* validate EVERY input */
goto badread;
printf ("Please provide last name : ");
if (scanf ("%9s", element->l_name) != 1)
goto badread;
printf ("Please provide score : ");
if (scanf ("%f", &element->score) != 1)
goto badread;
return element; /* return allocated and initialized element */
badread:; /* just a simple goto label for handling read error */
free (element); /* free memory of node if error */
return NULL;
}
(note: the use of goto to send you to a label beyond the normal return where you can free the memory for the node that failed during filling.)
/* initialize the list, don't worry about the elements */
Liste *initialize (void)
{
Liste *liste = malloc(sizeof *liste);
if (liste == NULL) {
perror ("malloc-liste"); /* give some meaningful error */
exit (EXIT_FAILURE);
}
liste->first = liste->last = NULL;
return liste;
}
void insert (Liste *liste, Element *nouveau)
{
if (liste == NULL || nouveau == NULL)
exit (EXIT_FAILURE);
if (!liste->first) /* inserting 1st node */
liste->first = liste->last = nouveau;
else { /* inserting all others */
liste->last->next = nouveau;
liste->last = nouveau;
}
}
(note: initialize and insert are straight-forward, the only two classes yo handle is whether you are inserting the 1st node, or all other nodes. Makes it very simple)
Putting it altogether, you can write your full test code as follows adding a function to iterate over the list printing values, and then a similar function to iterate over the list freeing nodes and then finally the list::
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 10 /* if you need a constant, #define one (or more) */
typedef struct Element Element;
struct Element {
char f_name[MAXN];
char l_name[MAXN];
float score;
Element* next;
};
typedef struct Liste Liste;
struct Liste {
Element *first, *last;
};
Element *read()
{
Element *element = malloc (sizeof(*element)); /* allocate */
if (element == NULL) /* validate */
return NULL;
element->next = NULL; /* initialize */
printf ("\nPlease provide first name : ");
if (scanf ("%9s", element->f_name) != 1) /* validate EVERY input */
goto badread;
printf ("Please provide last name : ");
if (scanf ("%9s", element->l_name) != 1)
goto badread;
printf ("Please provide score : ");
if (scanf ("%f", &element->score) != 1)
goto badread;
return element; /* return allocated and initialized element */
badread:; /* just a simple goto label for handling read error */
free (element); /* free memory of node if error */
return NULL;
}
/* initialize the list, don't worry about the elements */
Liste *initialize (void)
{
Liste *liste = malloc(sizeof *liste);
if (liste == NULL) {
perror ("malloc-liste"); /* give some meaningful error */
exit (EXIT_FAILURE);
}
liste->first = liste->last = NULL;
return liste;
}
void insert (Liste *liste, Element *nouveau)
{
if (liste == NULL || nouveau == NULL)
exit (EXIT_FAILURE);
if (!liste->first) /* inserting 1st node */
liste->first = liste->last = nouveau;
else { /* inserting all others */
liste->last->next = nouveau;
liste->last = nouveau;
}
}
void prnlist (Liste *liste)
{
Element *iter = liste->first;
while (iter) { /* just iterate list outputting values */
printf ("%-10s %-10s -> %.2f\n",
iter->f_name, iter->l_name, iter->score);
iter = iter->next;
}
}
void freelist (Liste *liste)
{
Element *iter = liste->first;
while (iter) {
Element *victim = iter;
iter = iter->next; /* iterate to next node BEFORE */
free (victim); /* you free victim */
}
free (liste);
}
int main (void) {
Liste *maListe = initialize(); /* create/initialize list */
Element *node;
while ((node = read())) /* allocate/read */
insert (maListe, node); /* insert */
puts ("\n\nElements in list:\n"); /* output list values */
prnlist (maListe);
freelist (maListe); /* don't forget to free what you allocate */
return 0;
}
Example Use/Output
$ ./bin/ll_liste
Please provide first name : Donald
Please provide last name : Duck
Please provide score : 99.2
Please provide first name : Minnie
Please provide last name : Mouse
Please provide score : 99.7
Please provide first name : Pluto
Please provide last name : Dog
Please provide score : 83.5
Please provide first name :
Elements in list:
Donald Duck -> 99.20
Minnie Mouse -> 99.70
Pluto Dog -> 83.50
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_liste
==10838== Memcheck, a memory error detector
==10838== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10838== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==10838== Command: ./bin/ll_liste
==10838==
Please provide first name : Donald
Please provide last name : Duck
Please provide score : 99.2
Please provide first name : Minnie
Please provide last name : Mouse
Please provide score : 99.6
Please provide first name : Pluto
Please provide last name : Dog
Please provide score : 87.2
Please provide first name :
Elements in list:
Donald Duck -> 99.20
Minnie Mouse -> 99.60
Pluto Dog -> 87.20
==10838==
==10838== HEAP SUMMARY:
==10838== in use at exit: 0 bytes in 0 blocks
==10838== total heap usage: 5 allocs, 5 frees, 144 bytes allocated
==10838==
==10838== All heap blocks were freed -- no leaks are possible
==10838==
==10838== For counts of detected and suppressed errors, rerun with: -v
==10838== 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 questions.

stacks with strings and integers - C

[3rd update]
I made some changes to David's code. I changed: n->next = s->top;s->top = n; to n->next = NULL;s->top = n; like H.S. suggested; and also added different features to the main function. I'm trying to get the char name and int value as input from the user while the program is running. The program should keep taking data from the user, storing it in the stack and allocating memory accordingly. The program only ends when the user types 'end' (char) or 0 (int). The code gets compiled, but that's it, after that, any input and the program stops. Also, am I wasting memory using fgets(str,20,stdin)? What if I don't use all that size. Any feedback is very much appreciated!
///PROGRAM STRUCTURE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stack_node{
int value;
char *name;
struct stack_node * next;
};
typedef struct stack_node stack_node;
struct stack{
stack_node *top;
int size;
};
typedef struct stack stack;
stack *create_stack()
{
stack *s = malloc (sizeof *s);
if (!s) {
perror ("malloc-s");
return NULL;
}
s->size = 0;
s->top = NULL;
return s;
}
int empty_stack (stack * s) {
return s->size == 0;
}
stack_node *push_stack (stack *s, int value, char *name) /// ****
{
stack_node *n = malloc (sizeof *n);
if (!n) {
perror ("malloc-n");
return NULL;
}
n->value = value;
n->name = malloc (strlen (name) + 1);
if (!n->name) {
perror ("malloc-name");
return NULL;
}
strcpy (n->name, name);
n->next = NULL; /// ****
s->top = n;
s->size++;
return n;
}
stack_node *pop_stack (stack *s)
{
if (s->size > 0) {
stack_node *node = s->top;
s->top = s->top->next;
s->size--;
return node;
}
return NULL;
}
void free_node (stack_node *n)
{
if (n->name)
free (n->name);
free (n);
}
void free_stack (stack *s)
{
while (s->size > 0) {
stack_node *victim = s->top;
s->top = s->top->next;
free_node (victim);
s->size--;
}
free (s);
}
int main (void) {
stack *s = create_stack();
stack_node *node = NULL;
char *str; ///NAME
int vs; ///VALUE
int i=0;
if (!s)
return 1;
do{
printf("NAME{%d]: ",i);
fgets(str,20,stdin); ///***?
printf("VALUE[%d]: ",i);
scanf(" %d",&vs);
if (push_stack (s, vs, str) == NULL) {
fprintf (stderr, "error: push node failed '%d'.\n", i);
return 1;
}
i++;
}while(str != 'end' || vs != 0);
i=0;
while ((node = pop_stack(s)) != NULL) {
printf ("value[%d]: %d name[%d]: %s\n", i,node->value, i,node->name);
i++;
free_node (node);
}
free_stack (s);
return 0;
}
///**********
do{
printf("NAME[%d]: ",i);
if(fgets(buf,sizeof buf,stdin)){
int rtn = sscanf (buf, "%63[^\n]", name);
if(rtn == 1){
printf("VALUE[%d]: ",i);
scanf(" %d",&vs);
}
}
push_stack(s,vs,name);
if (s == NULL) {
fprintf (stderr, "error: push node failed '%d'.\n", i);
return 1;
}
i++;
}while(node->name != "end" || node->value != 0);
Well.. It's obvious you have put effort into your code, but it is also obvious you are still struggling to wrap your head around storing a struct as a node within the stack (and with basic string handling issues).
First, there is no need to cast the return of malloc, see See: Do I cast the result of malloc?. Next, always size your allocation for an object based on the derefernced pointer to the object. For example stack *s declares s as a pointer to type stack. If s is a pointer to stack, then *s is type stack. Sizing your allocations with the actual pointer itself with eliminate any chance of an incorrect size. Further, it is your responsibility to validate every allocation by checking the return is not NULL. With that, your create_stack() could be:
stack *create_stack()
{
stack *s = malloc (sizeof *s); /* size from dereferenced pointer */
if (!s) { /* validate every allocation */
perror ("malloc-s");
return NULL;
}
s->size = 0;
s->top = NULL;
return s;
}
Your empty_stack is unused, but otherwise OK.
Your push_stack function is where a number of problems lie. First you cannot allocate for the node and the string at the same time! (even if you could, you would be one-byte too small as you have forgotten space for the nul-terminating character at the end of the string). You declare:
struct stack_node{
int value;
char *name;
struct stack_node * next;
};
You must allocate first for your node, then allocate storage for your pointer name -- remembering to validate the allocation as done in create_stack(), e.g.
stack_node *n = malloc (sizeof *n); /* allocate node only */
if (!n) { /* validate every allocation */
perror ("malloc-n");
return NULL;
}
n->value = value;
n->name = malloc (strlen (name) + 1); /* allocate strlen + 1 chars */
if (!n->name) { /* validate! */
perror ("malloc-name");
return NULL;
}
Next, you cannot assign strings in C (aside from initialization of a pointer to point to a string-literal, e.g. char *name = "foo";) In C, you must copy your string to the valid storage your create for n->name. For example:
strcpy (n->name, name); /* you cannot assign strings, use strcpy */
Further, how are you going to indicate to the caller, whether your allocations succeeded or failed if you declare push_stack as type void??
Always select a return type that can indicate success/failure where required. With that in mind, you can simply return a pointer to the new node (which is convenient for immediate use anyway), or return NULL on failure, e.g.
/* choose return type that can indicate success/failure */
stack_node *push_stack (stack *s, int value, const char *name)
{
stack_node *n = malloc (sizeof *n); /* allocate node only */
if (!n) { /* validate every allocation */
perror ("malloc-n");
return NULL;
}
n->value = value;
n->name = malloc (strlen (name) + 1); /* allocate strlen + 1 chars */
if (!n->name) { /* validate! */
perror ("malloc-name");
return NULL;
}
strcpy (n->name, name); /* you cannot assign strings, use strcpy */
n->next = s->top;
s->top = n;
s->size++;
return n;
}
You do not want two-separate pop function. Instead of pop_stack_value and pop_stack_name, you simply want a pop_stack that returns a pointer to the top node. You can then access either the value or name (e.g. node->value or node->name) as required.
It also makes sense to declare a couple of simple helper functions to free the memory allocated when it is no longer needed. You are dealing with nodes and the stack, so a free_node helper and a free_stack helper make sense. (and remember, you may want to free the stack before it is empty, so code the free_stack accordingly), e.g.
void free_node (stack_node *n) /* free node helper function */
{
if (n->name)
free (n->name);
free (n);
}
void free_stack (stack *s) /* free stack helper (need not be empty) */
{
while (s->size > 0) {
stack_node *victim = s->top;
s->top = s->top->next;
free_node (victim);
s->size--;
}
free (s);
}
Putting all the pieces together in a short example, you could do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stack_node{
int value;
char *name;
struct stack_node * next;
};
typedef struct stack_node stack_node;
struct stack{
stack_node *top;
int size;
};
typedef struct stack stack;
stack *create_stack()
{
stack *s = malloc (sizeof *s); /* size from dereferenced pointer */
if (!s) { /* validate every allocation */
perror ("malloc-s");
return NULL;
}
s->size = 0;
s->top = NULL;
return s;
}
int empty_stack (stack * s) { /* s->size is type 'int' */
return s->size == 0;
}
/* choose return type that can indicate success/failure */
stack_node *push_stack (stack *s, int value, const char *name)
{
stack_node *n = malloc (sizeof *n); /* allocate node only */
if (!n) { /* validate every allocation */
perror ("malloc-n");
return NULL;
}
n->value = value;
n->name = malloc (strlen (name) + 1); /* allocate strlen + 1 chars */
if (!n->name) { /* validate! */
perror ("malloc-name");
return NULL;
}
strcpy (n->name, name); /* you cannot assign strings, use strcpy */
n->next = s->top;
s->top = n;
s->size++;
return n;
}
stack_node *pop_stack (stack *s) /* return the node on pop */
{
if (s->size > 0) {
stack_node *node = s->top;
s->top = s->top->next;
s->size--;
return node; /* caller is responsible for freeing node */
}
return NULL;
}
void free_node (stack_node *n) /* free node helper function */
{
if (n->name)
free (n->name);
free (n);
}
void free_stack (stack *s) /* free stack helper (need not be empty) */
{
while (s->size > 0) {
stack_node *victim = s->top;
s->top = s->top->next;
free_node (victim);
s->size--;
}
free (s);
}
int main (void) {
const char *str[] = { "john", "jack", "jill", "sally", "sue" };
int n = sizeof str/sizeof *str;
stack *s = create_stack();
stack_node *node = NULL;
if (!s) /* validate !!! */
return 1;
for (int i = 0; i < n; i++)
if (push_stack (s, i+1, str[i]) == NULL) {
fprintf (stderr, "error: push node failed '%d'.\n", i);
return 1;
}
while ((node = pop_stack(s)) != NULL) {
printf ("value: %2d name: %s\n", node->value, node->name);
free_node (node); /* free node */
}
free_stack (s); /* free stack */
return 0;
}
Example Use/Output
$ ./bin/stack_struct
value: 5 name: sue
value: 4 name: sally
value: 3 name: jill
value: 2 name: jack
value: 1 name: john
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_struct
==4204== Memcheck, a memory error detector
==4204== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4204== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==4204== Command: ./bin/stack_struct
==4204==
value: 5 name: sue
value: 4 name: sally
value: 3 name: jill
value: 2 name: jack
value: 1 name: john
==4204==
==4204== HEAP SUMMARY:
==4204== in use at exit: 0 bytes in 0 blocks
==4204== total heap usage: 11 allocs, 11 frees, 161 bytes allocated
==4204==
==4204== All heap blocks were freed -- no leaks are possible
==4204==
==4204== For counts of detected and suppressed errors, rerun with: -v
==4204== 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.
Finally, to avoid most of these problems to begin with, always compile with warnings enabled, and do not accept code until it compiles cleanly without warning. To enable warnings add -Wall -Wextra to your gcc or clang compile string. (add -pedantic for several additional warnings). For clang, instead you can use -Weverything. For VS (cl.exe on windoze), add /W3 (or use /Wall but you will get quite a few extraneous windows non-code related warnings). Read and understand each warning. They will identify any problems, and the exact line on which they occur.
You can learn as much about coding by simply listening to what your compiler is telling you as you can from most tutorials.
Look things over and let me know if you have further questions.
Taking value Input, then name
When you take user input, consider using fgets for reading both the name and value (and then use sscanf (or strtol) to convert to int). You avoid many pitfalls with scanf and failure to empty stdin after a matching failure (which will generally lead to an infinite loop as well as Undefined Behavior)
You purposely want to loop continually until you validate your have the input you ask for -- and that it meets your requirements (range, etc...). You also must protect against the user canceling input by generating a manual EOF (e.g. Ctrl+d on Linux or Ctrl+z on windoze, but see: CTRL+Z does not generate EOF in Windows 10)
/* if you need constants, #define one (or more), or use a global enum */
#define MAXC 1024 /* max chars for input buffer (don't skimp) */
#define MAXNM 64 /* max length for name buffer (ditto) */
...
int main (void) {
stack *s = create_stack();
stack_node *node = NULL;
if (!s) /* validate !!! */
return 1;
for (;;) { /* loop continually taking input until exit conditon */
char buf[MAXC] = "", /* line buffer for fgets */
name[MAXNM] = ""; /* buffer for parsing name from line */
int value = 0; /* int to parse from line */
for (;;) { /* loop continually until you get valid int */
fputs ("\nenter value: ", stdout); /* prompt for value */
if (fgets (buf, MAXC, stdin)) { /* validate line read */
if (sscanf (buf, "%d", &value) == 1) { /* convert to int */
if (value == 0) { /* your exit condition */
fputs (" value == 0 (input done)\n", stderr );
goto inputdone; /* jump out of both loops */
}
break;
}
else /* non-integer (non-numeric) input */
fputs (" error: invalid integer input.\n", stderr);
}
else { /* fgets returned EOF (user canceled) */
fputs ("(user canceled input)\n", stderr);
goto inputdone; /* jump out of both loops */
}
}
for (;;) { /* loop continually until name recieved */
fputs ("enter name : ", stdout); /* prompt name */
if (fgets (name, MAXNM, stdin)) { /* read name */
size_t len = strlen (name); /* get length */
if (len && name[len-1] == '\n') { /* last char \n ? */
name[--len] = 0; /* overwrite with \0 */
if (len) /* check 1 char remains */
break; /* got good name, go push */
else /* user just pressed [Enter] */
fputs (" error: empty-line.\n", stderr);
}
else if (len == MAXNM -1) /* was input too long? */
fputs (" warning: name truncated.\n", stderr);
}
else { /* fgets returned EOF (user canceled) */
fputs ("(user canceled input)\n", stderr);
goto inputdone; /* jump out of both loops */
}
}
push_stack (s, value, name); /* push value and name on stack */
}
inputdone:;
puts ("\nvalues stored in stack\n");
while ((node = pop_stack(s)) != NULL) {
printf ("value: %2d name: %s\n", node->value, node->name);
free_node (node); /* free node */
}
free_stack (s); /* free stack */
return 0;
}
(note: it would probably be better to end input when the user presses Enter on an empty line rather than looking for a value == 0 (because an int can be zero))
Example Use/Output
$ ./bin/stack_struct_input_simple
enter value: 1
enter name : Frank Zappa
enter value: 2
enter name :
error: empty-line.
enter name : Jimmy Buffet
enter value: 3
enter name : Grahm Nash
enter value: 4
enter name : John Bonham
enter value: 0
value == 0 (input done)
values stored in stack
value: 4 name: John Bonham
value: 3 name: Grahm Nash
value: 2 name: Jimmy Buffet
value: 1 name: Frank Zappa
See if you can go though the input routine above and understand why it is written the way it is. (I also have one that will accept both value name or value then name) You can be as flexible as you want with input as long as you follow the rules and validate everything, protect against a manual EOF and always know what remains in your input buffer (e.g. stdin).
Is there any reason not to alter your stack_node struct to directly include the name.
struct stack_node{
int value;
struct stack_node * next;
char name[1];
};
This particular form has the advantage that if you call malloc as follows:
struct stack_node * new_node = malloc(sizeof *new_node + strlen(name));
you get exactly the correct amount of memory, including space for the trailing NUL byte on the string.
Ob warning. Skip the last paragraph if you are humor impaired.
Still here? Good. Failing all this, you could always try a Soap interface. But then perhaps we should never talk about that. ;)
Few of problems in your code:
Problem 1:
Look at these statement of function push_stack():
n->next = s->top;
s->top = n;
The next of stack node n is pointing to top of stack and top of stack is pointing back to node n. The next of the node created should be NULL as it will be top node of stack. You don't need this statement n->next = s->top;. It should be:
n->next = NULL;
s->top = n;
Problem 2:
In main(), you are doing:
printf("STACK: %d \n",pop_stack_value(s));
printf("STACK: %s \n", pop_stack_name(s));
and in function pop_stack_value(), you are doing:
stack_node * sn = s->top;
s->top = s->top->next;
free(sn);
s->top is pointing to the top of stack and the top of stack is pointing back to node (as you did in push_stack()). The free(sn) will free the top of stack. Now in function pop_stack_name(), you are doing
char * name = s->top->name;
stack_node * sn = s->top;
s->top = s->top->next;
s->size--;
free(sn);
s->top is pointing to memory which has been already freed in pop_stack_value() and you are trying to access memory which your program doesn't own. This is undefined behavior.
The way you are doing pop operation is completely wrong. In the pop operation of stack, you should pop the whole node instead of popping individual value of node. Once the node is popped, set the top of stack to point to the previous node. For that you need to traverse the whole list because you are using singly linked list. As an alternative, you can have doubly linked list in which every node is having its previous node address. With this resetting top of stack would not be an expensive operation.

Dynamic array of Strings inside a Struct realloc

I have an Array of Strings inside a Struct that looks like this
#define ID_LEN 20
struct Person{
char name[ID_LEN];
int num_items;
char **items;
};
int main(){
int num_persons = 10;
struct Person *ptr[num_persons];
I start with an array of 10 persons. Initially all persons have 0 items so their list of items is an malloc(0).
for(int i = 0; i < num_persons; i++){
ptr[i] = (struct Person *) malloc(sizeof(struct Person) + 1);
ptr[i]->num_items = 0;
ptr[i]->items = malloc(0 * sizeof(char*));
}
At some point I want to name those persons and add them some items like this.
strcpy(ptr[0]->name, "John");
ptr[0]->num_items = 1;
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));
strcpy(ptr[0]->items[0], "pencil");
printf("Name: %s\n", ptr[0]->name);
printf("Number of items: %d\n", ptr[0]->num_items);
for(int i = 0; i < ptr[0]->num_items; i++){
printf("Item %d is %s\n", i, ptr[0]->items[i]);
}
I'm getting segmentation fault: 11. I'm not sure if I have done realloc correctly or not.
You have several problems here
First your ID_LEN
nowhere do you check if by copying to name you surpass the ID_LEN
so instead of
strcpy(ptr[0]->name, "John");
use
strcpy_s(ptr[0]->name,sizeof(ptr[0]->name),"John or whatever"); // C11
You allocate
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
but
you should not cast return value of malloc/realloc (search on web for
explanation, it has been described ad nauseum)
when using realloc, you should first check the return value, it may fail.
char** tmp = realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
if (tmp != NULL)
{
ptr[0]->items = tmp;
}
else
{
abort();
}
The memory realloc returns is partly uninitialized (old pointers remain but
new ones are uninitialized. In your case you did not have any previous
pointers so that one items[0] is uninitialized.
so when you do
strcpy(ptr[0]->items[0], "pencil");
it will fail since items[0] is pointing to some arbitrary memory location.
after you realloc pointers you need initialize them to point to a
memory large enough to accommodate your string
E.g.
ptr[0]->items[0] = strdup("pencil"); // does malloc then copies string
It is not so efficient to use realloc everytime you need to add one new
item, instead allocate a bunch of items but set the ones you are not using
to NULL then keep track of how many are left, once they run out allocate
another bunch
All the problem in your code revolves around using memory which is not allocated.
Consider
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));//line 1
strcpy(ptr[0]->items[0], "pencil");//line 2
In line 1 you have allocated memory to hold ptr[0]->num_items number of pointers to c strings.But you have not actually allocated memory to store c strings i.e. those pointers are not pointing to actual memory.
When in line 2 you try to access ptr[0]->items[0] its just char* there is no memory allocated to it.
I added the below line before line 2 and your code worked fine.
for(int i=0;i<ptr[0]->num_items;i++)
ptr[0]->items[i]=malloc(sizeof(char)*10);
In addition to what the other answers provide, there are several other considerations relevant to what you are doing. The first, don't 0 allocate anything. That is a holdover from days gone by and not necessary.
Next, following your static declaration of a variable length array of num_persons pointers to type struct Person, you must decide whether you will allocate storage for all your pointers at once, or whether you will allocate storage for the struct, only when adding a person. That will have implications when you split your code up into functions.
Since the remainder of your data structure will be allocated dynamically, you must validate each allocation succeeds before copying/initializing, whatever.
When you allocate your empty structure, take a close look at calloc instead of malloc. This is one instance where the default initialization provided by calloc can help.
The remainder of your task is just an accounting problem -- that is accounting for which members require allocation, the current state and size of each allocation, and then finally to free the memory you have allocated when it is no longer needed.
Take for example the code you might want for adding a person with an item to your array of pointers. To validate the addition, you will need to know that ptr is a valid pointer and whether you have reached your num_persons limit before you begin to allocate memory. You next must allocate storage for a struct Person and assign the address for that block to ptx[X] (for whatever index X you are working with).
Once allocated, you can initialize defaults (or if calloc was used, know that all values were initialized to 0/NULL). Following that you can copy name (limited to ID_LEN) and allocate pointers and storage for each item you wish to hold in your pointer-to-pointer-to-char* items (which you will handle similar to ptr accept that the pointers for items are allocated as well) Note there is no need to realloc (items,... at this point as this is the first time items is allocated.
If you put all of that together, you can come up with a function to add a person together with their first item similar to the following, where max is the maximum number of persons allowed, nm is the name, itm is the item, and idx is a pointer to the current index for that person:
/* add person with item */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx)
{
if (!ptr || *idx + 1 == max) return NULL;
int i = *idx;
/* allocate storage for struct */
if (!(ptr[i] = calloc (1, sizeof **ptr))) {
fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i);
return NULL;
}
strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */
/* allocate/validate pointer to char* ptr[i]->items */
if (!(ptr[i]->items =
malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
(*idx)++;
return ptr[i];
}
(note: you must assign the return of the function to ptr[X] in the calling function. Also note, C-style recommends all lower-case names, which is what you see above, leave CamelCase names to C++)
Once you have a person added, you will want the ability to add items to the list of items associated with that person. This is where realloc comes in to allow you to resize the number of pointers-to-items for that person. You can do something similar to add person, but the index you pass no longer needs to be a pointer as it will not be updated within the function. e.g.
/* add item to person at index 'i' */
struct person *additem (struct person **ptr, int i, char *itm)
{
if (!ptr) return NULL;
void *tmp;
/* allocate/realloc/validate pointer to char* ptr[i]->items */
if (!(tmp = realloc (ptr[i]->items,
(ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->items = tmp; /* assign tmp on successful realloc */
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
return ptr[i];
}
You are now able to add both persons and items to the list, but need a way to iterate over all values in the list if you are going to make use of them. Whether searching, printing, or freeing memory, all iteration functions will have similar form. To print all persons and items you could do something similar to the following:
/* print the list of persons and items */
void prndetail (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items);
for (i = 0; i < ptr[p]->num_items; i++)
printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]);
putchar ('\n');
}
}
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. When you are done with your list, you will need to free it. Similar to printing, it can be done as follows:
/* free all allocated memory */
void deletelist (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
for (i = 0; i < ptr[p]->num_items; i++)
free (ptr[p]->items[i]);
free (ptr[p]->items);
free (ptr[p]);
}
// free (ptr); /* if array of pointers allocated, not static */
}
That's really all there is to the accounting lesson. If you keep track of each part of the data structure you are using, managing the memory is straight forward. Putting all the pieces together in a short example, you could do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { NPER = 10, ID_LEN = 20 };
struct person {
char name[ID_LEN];
int num_items;
char **items;
};
/* add person + item - to add both person and item at once */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx);
/* add item to existing person */
struct person *additem (struct person **ptr, int i, char *itm);
void prndetail (struct person **ptr, int idx);
void deletelist (struct person **ptr, int idx);
int main (void) {
int idx = 0;
struct person *ptr[NPER];
/* allocate storage for struct, add person + item */
if (!(ptr[idx] = addpwitem (ptr, NPER, "John", "pencils", &idx))) {
fprintf (stderr, "error: adding ptr[%d] failed.\n", idx);
return 1;
}
printf ("\nadded John:\n");
prndetail (ptr, idx); /* print contents of persons & items */
additem (ptr, idx - 1, "pens"); /* add next item */
printf ("\nadded item 'pens' for John:\n");
prndetail (ptr, idx); /* print contents of persons & items */
/* add next person + item */
if (!(ptr[idx] = addpwitem (ptr, NPER, "Martha", "paper", &idx))) {
fprintf (stderr, "error: adding ptr[%d] failed.\n", idx);
return 1;
}
printf ("\nadded Martha:\n");
prndetail (ptr, idx); /* print contents of persons & items */
deletelist (ptr, idx); /* free all allocated memory */
return 0;
}
/* add person with item */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx)
{
if (!ptr || *idx + 1 == max) return NULL;
int i = *idx;
/* allocate storage for struct */
if (!(ptr[i] = calloc (1, sizeof **ptr))) {
fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i);
return NULL;
}
strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */
/* allocate/validate pointer to char* ptr[i]->items */
if (!(ptr[i]->items =
malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
(*idx)++;
return ptr[i];
}
/* add item to person at index 'i' */
struct person *additem (struct person **ptr, int i, char *itm)
{
if (!ptr) return NULL;
void *tmp;
/* allocate/realloc/validate pointer to char* ptr[i]->items */
if (!(tmp = realloc (ptr[i]->items,
(ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->items = tmp; /* assign tmp on successful realloc */
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
return ptr[i];
}
/* print the list of persons and items */
void prndetail (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items);
for (i = 0; i < ptr[p]->num_items; i++)
printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]);
putchar ('\n');
}
}
/* free all allocated memory */
void deletelist (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
for (i = 0; i < ptr[p]->num_items; i++)
free (ptr[p]->items[i]);
free (ptr[p]->items);
free (ptr[p]);
}
// free (ptr); /* if array of pointers allocated */
}
Example Use/Output
$ ./bin/struct_p2p2c
added John:
John 1 pencils
added item 'pens' for John:
John 2 pencils, pens
added Martha:
John 2 pencils, pens
Martha 1 paper
Memory Error Check
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.
It is simple to do:
$ valgrind ./bin/struct_p2p2c
==7618== Memcheck, a memory error detector
==7618== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7618== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==7618== Command: ./bin/struct_p2p2c
==7618==
added John:
John 1 pencils
added item 'pens' for John:
John 2 pencils, pens
added Martha:
John 2 pencils, pens
Martha 1 paper
==7618==
==7618== HEAP SUMMARY:
==7618== in use at exit: 0 bytes in 0 blocks
==7618== total heap usage: 8 allocs, 8 frees, 115 bytes allocated
==7618==
==7618== All heap blocks were freed -- no leaks are possible
==7618==
==7618== For counts of detected and suppressed errors, rerun with: -v
==7618== 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.
Look over this and the rest of the answers. There are many good suggestions between them. Let us know if you have further questions.
You are allocating for the list of items, but you are not allocating space for each individual item string. Your
strcpy(ptr[0]->items[0], "pencil");
will fail.

Free list implementation not working properly

So, I have been assigned a task to implement a free list. A list where items that are going to be free:d are added to and later the list is free:d in one go. I have written the following:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct list_t list_t;
struct list_t {
list_t* succ;
list_t* pred;
void* data;
};
list_t* free_list;
void free_list_memory(void)
{
list_t *p , *q;
p = free_list;
while (p != NULL) {
q = p->succ;
free(p);
p = q;
}
}
void add_to_free_list(list_t* add) {
if (free_list != NULL) {
add->pred = free_list->pred;
add->succ = free_list;
free_list->pred->succ = add;
free_list->pred = add;
} else {
free_list = add;
add->succ = add;
add->pred = add;
}
}
static double sec(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + 1e-6 * tv.tv_usec;
}
int empty(list_t* list)
{
return list == list->succ;
}
list_t *new_list(void* data)
{
list_t* list;
list = malloc(sizeof(list_t));
assert(list != NULL);
list->succ = list->pred = list;
list->data = data;
return list;
}
void add(list_t* list, void* data)
{
list_t* link;
list_t* temp;
link = new_list(data);
list->pred->succ= link;
link->succ = list;
temp = list->pred;
list->pred = link;
link->pred = temp;
}
void take_out(list_t* list)
{
list->pred->succ = list->succ;
list->succ->pred = list->pred;
list->succ = list->pred = list;
}
void* take_out_first(list_t* list)
{
list_t* succ;
void* data;
if (list->succ->data == NULL)
return NULL;
data = list->succ->data;
succ = list->succ;
take_out(succ);
free(succ);
return data;
}
static size_t nextsize()
{
#if 1
return rand() % 4096;
#else
size_t size;
static int i;
static size_t v[] = { 24, 520, 32, 32, 72, 8000, 16, 24, 212 };
size = v[i];
i = (i + 1) % (sizeof v/ sizeof v[0]);
return size;
#endif
}
static void fail(char* s)
{
fprintf(stderr, "check: %s\n", s);
abort();
}
int main(int ac, char** av)
{
int n = 50; /* mallocs in main. */
int n0;
list_t* head;
double begin;
double end;
double t = 2.5e-9;
if (ac > 1)
n = atoi(av[1]);
n0 = n;
head = new_list(NULL);
printf("check starts\n");
begin = sec();
while (n > 0) {
add(head, malloc(nextsize()));
n -= 1;
if ((n & 1) && !empty(head)) {
add_to_free_list(take_out_first(head)); //before free(take_out_first(head))
}
}
printf("Done");
while (!empty(head))
add_to_free_list(take_out_first(head)); //before free(take_out_first(head))
free_list_memory(); //added line
end = sec();
printf("check is ready\n");
printf("total = %1.3lf s\n", end-begin);
printf("m+f = %1.3g s\n", (end-begin)/(2*n0));
printf("cy = %1.3lf s\n", ((end-begin)/(2*n0))/t);
return 0;
}
Running the code gives:
a.out(10009,0x7fff79ec0300) malloc: *** error for object 0x7fe160c04b20: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
DoneAbort trap: 6
Running in valgrind gives:
check starts
==10011== Invalid read of size 8
==10011== at 0x1000009B8: free_list_memory (check.c:20)
==10011== by 0x100000D8B: main (check.c:163)
==10011== Address 0x10081e270 is 0 bytes inside a block of size 423 free'd
==10011== at 0x10000894F: free (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==10011== by 0x1000009CA: free_list_memory (check.c:21)
==10011== by 0x100000D8B: main (check.c:163)
==10011==
==10011== Invalid free() / delete / delete[] / realloc()
==10011== at 0x10000894F: free (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==10011== by 0x1000009CA: free_list_memory (check.c:21)
==10011== by 0x100000D8B: main (check.c:163)
==10011== Address 0x10081e270 is 0 bytes inside a block of size 423 free'd
==10011== at 0x10000894F: free (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==10011== by 0x1000009CA: free_list_memory (check.c:21)
==10011== by 0x100000D8B: main (check.c:163)
==10011==
==10011==
==10011== More than 10000000 total errors detected. I'm not reporting any more.
==10011== Final error counts will be inaccurate. Go fix your program!
==10011== Rerun with --error-limit=no to disable this cutoff. Note
==10011== that errors may occur in your program without prior warning from
==10011== Valgrind, because errors are no longer being displayed.
==10011==
^C==10011==
==10011== HEAP SUMMARY:
==10011== in use at exit: 38,785 bytes in 423 blocks
==10011== total heap usage: 602 allocs, 6,176,342 frees, 149,153 bytes allocated
==10011==
==10011== LEAK SUMMARY:
==10011== definitely lost: 0 bytes in 0 blocks
==10011== indirectly lost: 0 bytes in 0 blocks
==10011== possibly lost: 0 bytes in 0 blocks
==10011== still reachable: 4,120 bytes in 2 blocks
==10011== suppressed: 34,665 bytes in 421 blocks
==10011== Rerun with --leak-check=full to see details of leaked memory
==10011==
==10011== For counts of detected and suppressed errors, rerun with: -v
==10011== ERROR SUMMARY: 10000000 errors from 2 contexts (suppressed: 0 from 0)
Now, I don't understand what's wrong since I am removing elements from the list that have been created using malloc...
As WhozCraig diagnosed, the problem is more that the code does not stop when it gets to the end of the free list because it is a circular list and the termination condition is looking for null pointers that don't exist.
You can fix most of the problems by rewriting free_list_memory() as:
static void free_list_memory(void)
{
if (free_list == 0)
return;
list_t *p = free_list;
do
{
list_t *q = p->succ;
// free(p->data); // Removed: see commentary below!
free(p);
p = q;
} while (p != free_list);
}
With that change, I get one unfreed memory block of size 24 (on a 64-bit build). So there is one list_t not being freed. That item is in head; it happens to have a NULL data pointer, so you can fix that final leak with:
free(head);
at the end of main().
Surprise!
At one time, I commented:
You also need to free the data as well as the list entry.
Somewhat to my surprise, on retesting, that isn't necessary. In fact, on my machine, in free_list_memory(), all the data pointers are null. That's actually unfortunate — malloc() is returning zeroed data. What actually happens is that the list freeing code releases the original list_t pointer that held the data, and then adds the original data pointer to the free list, treating it as a list_t structure. That gets iffy if the size of the allocated block (a random number from nextsize()) is smaller than a list_t.
To fix the problems and get a really clean run, even with different numbers of entries, I created this instrumented version of the code (with WhozCraig's function active).
Revised code
Note that the code ensures that the space allocated is at least as big as a list_t structure; that is crucial to avoid memory access errors (on longer runs; I used 200 to get problems with small allocations without adding sizeof(list_t) because it so happens that the first 50 allocations are always big enough).
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
typedef struct list_t list_t;
struct list_t {
list_t* succ;
list_t* pred;
void* data;
};
list_t* free_list;
static
void free_list_memory(void)
{
if (!free_list)
return;
// terminate "last" node (the node that refers
// back to the node pointed to by free_list,
// including a potential self-referencing node)
free_list->pred->succ = NULL;
// now run the loop. uses free_list as the iterator
// as it will finish with NULL, as it will be empty.
while (free_list)
{
void *p = free_list;
if (free_list->data == 0)
printf("2 %p: data is null\n", free_list);
//free(free_list->data); // Not necessary
free_list = free_list->succ;
free(p);
}
}
#if 0
static void free_list_memory(void)
{
if (free_list == 0)
return;
list_t *p = free_list;
do
{
list_t *q = p->succ;
// free(p->data); // Removed: see commentary below!
free(p);
p = q;
} while (p != free_list);
}
#endif
#if 0
/* Broken code from question - does not handle circular list */
void free_list_memory(void)
{
list_t *p , *q;
p = free_list;
while (p != NULL) {
q = p->succ;
free(p);
p = q;
}
}
#endif
static
void add_to_free_list(list_t* add) {
if (free_list != NULL) {
add->pred = free_list->pred;
add->succ = free_list;
free_list->pred->succ = add;
free_list->pred = add;
} else {
free_list = add;
add->succ = add;
add->pred = add;
}
add->data = 0; // Added to avoid access to uninitialized data warning from valgrind
}
static double sec(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + 1e-6 * tv.tv_usec;
}
static
int empty(list_t* list)
{
return list == list->succ;
}
static
list_t *new_list(void* data)
{
list_t* list;
list = malloc(sizeof(list_t));
printf("1: data address is %p\n", data);
assert(list != NULL);
list->succ = list->pred = list;
list->data = data;
return list;
}
static
void add(list_t* list, void* data)
{
list_t* link;
list_t* temp;
link = new_list(data);
list->pred->succ= link;
link->succ = list;
temp = list->pred;
list->pred = link;
link->pred = temp;
}
static
void take_out(list_t* list)
{
list->pred->succ = list->succ;
list->succ->pred = list->pred;
list->succ = list->pred = list;
}
static
void* take_out_first(list_t* list)
{
list_t* succ;
void* data;
if (list->succ->data == NULL)
{
printf("3: %p - data is null\n", list->succ);
return NULL;
}
data = list->succ->data;
succ = list->succ;
take_out(succ);
free(succ);
return data;
}
static size_t nextsize(void)
{
#if 1
size_t v = rand() % 4096 + sizeof(list_t);
printf("Size: %zu\n", v);
return v;
//return rand() % 4096;
#else
size_t size;
static int i;
static size_t v[] = { 24, 520, 32, 32, 72, 8000, 16, 24, 212 };
size = v[i];
i = (i + 1) % (sizeof v/ sizeof v[0]);
return size;
#endif
}
#if 0
static void fail(char* s)
{
fprintf(stderr, "check: %s\n", s);
abort();
}
#endif
int main(int ac, char** av)
{
int n = 50; /* mallocs in main. */
int n0;
list_t* head;
double begin;
double end;
double t = 2.5e-9;
if (ac > 1)
n = atoi(av[1]);
n0 = n;
head = new_list(NULL);
printf("check starts\n");
begin = sec();
while (n > 0) {
add(head, malloc(nextsize()));
n -= 1;
if ((n & 1) && !empty(head)) {
add_to_free_list(take_out_first(head)); //before free(take_out_first(head))
}
}
printf("Done\n");
while (!empty(head))
add_to_free_list(take_out_first(head)); //before free(take_out_first(head))
free_list_memory(); //added line
free(head);
end = sec();
printf("Check is ready\n");
printf("total = %1.3lf s\n", end-begin);
printf("m+f = %1.3g s\n", (end-begin)/(2*n0));
printf("cy = %1.3lf s\n", ((end-begin)/(2*n0))/t);
return 0;
}
The compilation options I habitually use (here I suppressed optimization to avoid having everything inlined in main()) require declarations of non-static functions, so I added static to each function except main() that was not already static. I also added (void) in place of () when necessary.
$ gcc -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror crashing.c -o crashing
$
Valgrind output
$ valgrind --leak-check=full --suppressions=suppressions crashing 10
==41722== Memcheck, a memory error detector
==41722== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==41722== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info
==41722== Command: crashing 10
==41722==
--41722-- UNKNOWN mach_msg unhandled MACH_SEND_TRAILER option
--41722-- UNKNOWN mach_msg unhandled MACH_SEND_TRAILER option (repeated 2 times)
--41722-- UNKNOWN mach_msg unhandled MACH_SEND_TRAILER option (repeated 4 times)
1: data address is 0x0
check starts
Size: 447
1: data address is 0x10083f3e0
Size: 2825
1: data address is 0x100842440
Size: 3313
1: data address is 0x100842f90
Size: 3138
1: data address is 0x100843cd0
Size: 1946
1: data address is 0x10083f760
Size: 2784
1: data address is 0x100844960
Size: 3824
1: data address is 0x100845480
Size: 2582
1: data address is 0x100846410
Size: 3931
1: data address is 0x100846ed0
Size: 1125
1: data address is 0x100847ed0
Done
2 0x10083f3e0: data is null
2 0x100842440: data is null
2 0x100842f90: data is null
2 0x100843cd0: data is null
2 0x10083f760: data is null
2 0x100844960: data is null
2 0x100845480: data is null
2 0x100846410: data is null
2 0x100846ed0: data is null
2 0x100847ed0: data is null
Check is ready
total = 0.010 s
m+f = 0.000487 s
cy = 194959.641 s
==41722==
==41722== HEAP SUMMARY:
==41722== in use at exit: 39,132 bytes in 430 blocks
==41722== total heap usage: 528 allocs, 98 frees, 71,359 bytes allocated
==41722==
==41722== LEAK SUMMARY:
==41722== definitely lost: 0 bytes in 0 blocks
==41722== indirectly lost: 0 bytes in 0 blocks
==41722== possibly lost: 0 bytes in 0 blocks
==41722== still reachable: 26,034 bytes in 311 blocks
==41722== suppressed: 13,098 bytes in 119 blocks
==41722== Reachable blocks (those to which a pointer was found) are not shown.
==41722== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==41722==
==41722== For counts of detected and suppressed errors, rerun with: -v
==41722== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 15)
$
And you can see from the printed pointer values that the data added to the free list is indeed the data rather than the list_t structures holding the data.
It would be more conventional (and arguably sensible) to simply move the entire list_t structure from the active list to the free list, rather than freeing the list_t structure and using the data as the list_t structure on the free list.
Also, you should probably record the size of the data so you know whether the stuff on the free list can be used for a particular allocation.
Assuming the rest of your code is correct (and that is a big assumption), your free_memory_list function doesn't account for the fact the list is circular. The last node succ member refers back to the address held in free_list.
If that is the case, this is more akin to what you need:
void free_list_memory(void)
{
if (!free_list)
return;
// terminate "last" node (the node that refers
// back to the node pointed to by free_list,
// including a potential self-referencing node)
free_list->pred->succ = NULL;
// now run the loop. uses free_list as the iterator
// as it will finish with NULL, as it will be empty.
while (free_list)
{
void *p = free_list;
free_list = free_list->succ;
free(p);
}
}
I have to be honest. I did not diagnose the rest of this, but this is a glaring problem if this list is indeed circular.
Best of luck.
Why are you using double linked (closed loop) list for your free list, if only go in one direction.
Your void free_list_memory(void) function crashes when trying to run a second time over your list.
Maybe this helps:
void add_to_free_list(list_t* add) {
if (free_list != NULL) {
free_list->succ = add;
add->succ = NULL;
} else {
free_list = add;
add->succ = NULL;
}
}

C free function (not using malloc)

I'm writing my own memory allocation program (without using malloc) and now I'm stuck with the free function (asfree in my code). I believe the functionality for the allocation is all there, the only problem lays on the free function. So by running the code below I can allocate 32 blocks: each block has a size of 48 + 16 (size of header).
So how can I deallocate/free all of them just after I have allocated them? Could you have a look at my free function and point me at the right direction?
P.S.: This is for learning purposes. I'm trying to get my head around structs, linked lists, memory allocations.
Thanks in advance.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 2048
typedef struct blk_struct
{
size_t size_blk;
struct blk_struct *next;
char data[0];
}blk_struct;
struct blk_struct *first = NULL;
static char buffer[BUFFER_SIZE];
void *asalloc(size_t size)
{
int nunits = (size + sizeof(blk_struct));
static int val = 1;
blk_struct *block, *current;
//locate position for block
if(first == NULL)
{
block = (blk_struct *)&buffer[0];
// Sanity check size.
if(nunits > BUFFER_SIZE)
return NULL;
// Initialise structure contents.
block->size_blk = size;
block->next = NULL;
// Add to linked list.
first = block;
// Give user their pointer.
return block->data;
}
//create a free list and search for a free block
for(current = first; current != NULL; current = current->next)
{
// If this element is the last element.
if(current->next == NULL)
{
block = (blk_struct *) (current->data + current->size_blk);
// Check we have space left to do this allocation
if((blk_struct *) &block->data[size] >
(blk_struct *) &buffer[BUFFER_SIZE])
{
printf("No more space\n");
return NULL;
}
// Initialise structure contents.
block->size_blk = size;
block->next = NULL;
// Add to linked list.
current->next = block;
// Give user their pointer.
return block->data;
}
}
printf("List Error\n");
return NULL;
}
// 'Free' function. blk_ptr = pointer to the block of memory to be released
void asfree(void *blk_ptr)
{
struct blk_struct *ptr = first;
struct blk_struct *tmp = NULL;
while(ptr != NULL)
{
if(ptr == blk_ptr)
{
printf("Found your block\n");
free(blk_ptr);
break;
}
tmp = ptr;
ptr = ptr->next;
}
}
// Requests fixed size pointers
int test_asalloc(void)
{
void *ptr = NULL;
int size = 48;
int i = 1;
int total = 0;
do
{
ptr = asalloc(size);
if(ptr != NULL)
{
memset(ptr, 0xff, size);
printf("Pointer %d = %p%s", i, ptr, (i % 4 != 0) ? ", " : "\n");
// each header needs 16 bytes: (sizeof(blk_struct))
total += (size + sizeof(blk_struct));
i++;
}
asfree(ptr); // *** <--- Calls the 'free' function ***
}
while(ptr != NULL);
printf("%d expected %zu\nTotal size: %d\n", i - 1,
BUFFER_SIZE / (size + sizeof(blk_struct)), total);
}
int main(void)
{
test_asalloc();
return 0;
}
I can see some problems with your asfree.
You need to subtract sizeof(blk_struct) when looking for block head. As allocated data the user wants to free are right behind the header and you have pointer to that data, not the header.
The second problem is what to do when you get the header. You cannot just call free on the data. You need to have some flag in the header and mark the block as free. And next time you try to allocate a block you need to be able to reuse free blocks, not just creating new blocks at the end. It is also good to be able to split a large free block into two smaller. To avoid fragmentation is is needed to merge neighbour free blocks to one larger.
Currently I am writing an OS as a school project. I recommend you to use simple alocator working like this:
Blocks of memory have headers containing links to neighbour blocks and free flag.
At the beginning there is one large block with free flag covering whole memory.
When malloc is called, blocks are searched from first to last. When large enough block is found it is split into two(allocated one and free reminder). Pointer to allocated block data is returned.
When free is called, matching block is marked as free. If neighbour blocks are also free they are merged.
I think this is the basic to be able to do malloc and free without fragmentation and loosing memory.
This is how a structure of headers and footer can look like:
// Header of a heap block
typedef struct {
// Size of the block including header and footer
size_t size;
// Indication of a free block
bool free;
// A magic value to detect overwrite of heap header.
uint32_t magic;
} heap_block_head_t;
// Footer of a heap block
typedef struct {
// A magic value to detect overwrite of heap footer.
uint32_t magic;
// Size of the block
size_t size;
} heap_block_foot_t;
The heap full of blocks with headers and footers like the one above is much like a linked list. Blocks do not describe their neighbours explicitly, but as long as you now they are there you can find them easily. If you have a position of one header then you can add block size to that position and you have a position of a header of the next block.
// Get next block
heap_block_head_t *current = ....
heap_block_head_t *next = (heap_block_head_t*)(((void*) current) + current->size);
// Get previous block
heap_block_head_t *current = ....
heap_block_foot_t *prev_foot = (heap_block_foot_t*)(((void*) current) - sizeof(heap_block_foot_t));
heap_block_head_t *prev = (heap_block_head_t*)(((void*) prev_foot) + sizeof(heap_block_foot_t) - prev_foot->size);
// Not sure if this is correct. I just wanted to illustrate the idea behind.
// Some extra check for heap start and end are needed
Hope this helps.

Resources