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.
Related
In a struct Node type, there is a char* data. And each node uses a linked list to combine.
If the type of "data" is INT it is ok. (ex. the age: 23,45,33....) But when the type turn to "char * ", ex. save name: “Jack”,"Jay","Jame". The value is all the same, the later will cover the fronter. ex:
First time input: Jack. The list is Jack
Second time input: Jay. The list is Jay Jay
Third time input:Jame. The list is Jame Jame Jame
The code is like:
#include <stdio.h>
#include <stdlib.h>
typedef struct listNode{
char *data;
struct listNode *next;
} *ListNodePtr;
typedef struct list {
ListNodePtr head;
} List;
List new_list(){
List temp;
temp.head = NULL;
return temp;
}
//Student Courses
void insert_at_front(ListNodePtr* head, char *data){
ListNodePtr new_node = malloc(sizeof(struct listNode));
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
void print_list(List *self)
{
ListNodePtr current = self->head;
while(current!=NULL)
{
printf("%s \n", current->data);
current = current->next;
}
printf("\n");
}
int main()
{
char i = 'y';
char *name;
List mylist = new_list();
while(i=='y'){
printf("your name is :");
scanf("%s",name);
insert_at_front(&mylist.head,name);
print_list(&mylist);
}
return 0;
}
The good news is your thinking on list operation is not too far off... the bad news is it isn't right-on either. The biggest stumbling blocks you have are just handling the basics of user-input, and insuring your have storage for each bit of data you want to store.
In main() when you declare char *name;, name is an uninitialized pointer. It does not point to any valid memory yet in which you can store the characters that make up name (plus the terminating nul-character). The only thing you can store in a pointer, is a memory address -- and, to be useful, that memory address must be the start of a valid block of memory sufficiently sized to hold what it is you are attempting to store.
In your code, since name does not point to any valid block of memory capable of storing the characters in name, you immediately invoke Undefined Behavior with scanf("%s",name); (Boom! - "Game Over" for your program).
Since you are reading a name, rarely over 64 characters, just use a fixed-size buffer to hold name to pass it to insert_at_front. Don't skimp on buffer size, so just to be sure, you can use something reasonable like 512 bytes. Don't use magic numbers in your code, so if you need a constant:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 512 /* if you need a constant, #define one (or more) */
...
int main (void) {
char i = 'y';
char name[MAXN] = ""; /* fixed size buf for reading name input */
...
Now for your structs, you are using a "wrapper" struct holding head that essentially wraps your list. It is fine to do, though not required. However, since you are using one, you need to either declare it using automatic storage (and pass it as a parameter) or dynamically allocate for it. Since you use a new_list function, if you were using automatic storage, you would have to declare mylist as type List in main() and pass it as a parameter to new_list() for initialization.
Why? You cannot return temp from new_list because temp was declared within new_list (it is local to new_list) and the function stack for new_list (the memory that contains the temp variable) is destroyed (released for reuse) when new_list returns.
If you want to return a pointer from new_list, then you must either (1) pass a previously declared temp from main() to new_list -- or -- (2) dynamically allocate for temp in new_list giving temp allocated storage duration so that the memory containing temp survives until free() is called on that memory, or the program ends. The normal option is (2), though there is nothing wrong with (1) if you adequately account for the storage duration of the variable.
Makes a few tweaks, and avoiding typedeffing a pointer, because It is NOT a good idea to typedef pointers?, and adding a convenient example of the flexibility you have to track list statistics in your wrapper struct, you could do something similar to:
typedef struct lnode { /* don't typedef pointers -- it will confuse you */
char *data;
struct lnode *next;
} lnode;
typedef struct list { /* you pass this list to insert_at_front */
lnode *head;
size_t size; /* you can track any list stats you like */
} list;
/* create a dynamically allocated list struct */
list *new_list (void)
{
list *temp = malloc (sizeof *temp); /* create storage for list */
if (!temp) { /* validate ALL allocations */
perror ("malloc-new_list");
return NULL;
}
temp->head = NULL; /* initialize head NULL */
temp->size = 0;
return temp; /* return pointer to new list */
}
To help make lists more logical while you are learning, it often helps to create a separate function that is responsible for creating each node you add to your list. This allows you to concentrate on which items within your node struct need storage allocated, and provides a convenient place to handle the allocation (and validation of that allocation), as well as initializing all values in a single place -- without cluttering your list logic. You could implement a create_node function similar to:
/* create new dynamically allocated node, initialize all values */
lnode *create_new_node (const char *data)
{
lnode *new_node = NULL;
if (!data) { /* validate data not NULL */
fputs ("error: data is NULL in create_new_node.\n", stderr);
return NULL;
}
new_node = malloc (sizeof *new_node); /* allocate/validate node */
if (!new_node) {
perror ("malloc-new_node");
return NULL;
}
/* allocate/validate storage for data */
if (!(new_node->data = malloc (strlen (data) + 1))) {
perror ("malloc-new_node->data");
free (new_node);
return NULL;
}
strcpy (new_node->data, data); /* copy data to new_node->data */
new_node->next = NULL; /* set next pointer NULL */
return new_node; /* return pointer to new_node */
}
(note: you have allocated for (1) the list, (2) the node, and (3) the data)
That leaves your logic for insert_at_front clean and readable. Additionally, you need to always use a proper type for any list operation, especially where any allocation is involved, that allows you to gauge success/failure of the list operation. Generally returning a pointer to the node added (a new head here) or NULL on failure, is all you need, e.g..
/* insert new node at front, returning pointer to head
* to guage success/failure of addition.
*/
lnode *insert_at_front (list *mylist, const char *data)
{
lnode *new_node = create_new_node(data);
if (!new_node)
return NULL;
new_node->next = mylist->head;
mylist->head = new_node;
mylist->size++;
return mylist->head;
}
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. Get in the habit now to cleaning up after yourself -- it will pay big dividends as your programs become more complex. If you are dealing with a list, and nodes, then write a function to free all data, nodes and the list when you are done with it. Something simple is all that is needed, e.g.
/* you are responsible for freeing any memory you allocate */
void free_list (list *mylist)
{
lnode *current = mylist->head;
while (current) {
lnode *victim = current;
current = current->next;
free (victim->data);
free (victim);
}
free (mylist);
}
With user input, your are responsible for validating that you received good input and that it satisfies any conditions you have placed on the input. You are also responsible for insuring that the state of the input buffer is ready for the next input operation. That means clearing any extraneous characters that may be left in the input buffer (e.g. stdin) that would cause your next attempt at input to fail. A simple helper function to empty stdin can save you from a world of trouble.
/* you are responsible for the state of stdin when doing user input */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
Further, every input function you will use has a return. You must validate the return of any input function you use to determine if (1) valid input was read, (2) whether the user canceled input by generating a manual EOF, and (3) when using scanf whether a matching or input failure occurred. So always check the return! For example:
while (i == 'y' || i == '\n') { /* 'y' or (default '\n') */
fputs ("\nenter name: ", stdout);
if (scanf ("%511[^\n]", name) != 1) { /* ALWAYS CHECK RETURN! */
fputs ("error: invalid input or user canceled.", stderr);
return 1;
}
empty_stdin(); /* empty any chars that remain in stdin */
...
Putting it altogether in a short example, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 512 /* if you need a constant, #define one (or more) */
typedef struct lnode { /* don't typedef pointers -- it will confuse you */
char *data;
struct lnode *next;
} lnode;
typedef struct list { /* you pass this list to insert_at_front */
lnode *head;
size_t size; /* you can track any list stats you like */
} list;
/* create a dynamically allocated list struct */
list *new_list (void)
{
list *temp = malloc (sizeof *temp); /* create storage for list */
if (!temp) { /* validate ALL allocations */
perror ("malloc-new_list");
return NULL;
}
temp->head = NULL; /* initialize head NULL */
temp->size = 0;
return temp; /* return pointer to new list */
}
/* create new dynamically allocated node, initialize all values */
lnode *create_new_node (const char *data)
{
lnode *new_node = NULL;
if (!data) { /* validate data not NULL */
fputs ("error: data is NULL in create_new_node.\n", stderr);
return NULL;
}
new_node = malloc (sizeof *new_node); /* allocate/validate node */
if (!new_node) {
perror ("malloc-new_node");
return NULL;
}
/* allocate/validate storage for data */
if (!(new_node->data = malloc (strlen (data) + 1))) {
perror ("malloc-new_node->data");
free (new_node);
return NULL;
}
strcpy (new_node->data, data); /* copy data to new_node->data */
new_node->next = NULL; /* set next pointer NULL */
return new_node; /* return pointer to new_node */
}
/* insert new node at front, returning pointer to head
* to guage success/failure of addition.
*/
lnode *insert_at_front (list *mylist, const char *data)
{
lnode *new_node = create_new_node(data);
if (!new_node)
return NULL;
new_node->next = mylist->head;
mylist->head = new_node;
mylist->size++;
return mylist->head;
}
/* print_list - tweaked for formatted output */
void print_list (list *self)
{
lnode *current = self->head;
while (current != NULL)
{
if (current == self->head)
printf (" %s", current->data);
else
printf (", %s", current->data);
current = current->next;
}
putchar ('\n');
}
/* you are responsible for freeing any memory you allocate */
void free_list (list *mylist)
{
lnode *current = mylist->head;
while (current) {
lnode *victim = current;
current = current->next;
free (victim->data);
free (victim);
}
free (mylist);
}
/* you are responsible for the state of stdin when doing user input */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
char i = 'y';
char name[MAXN] = ""; /* fixed size buf for reading name input */
list *mylist = new_list();
while (i == 'y' || i == '\n') { /* 'y' or (default '\n') */
fputs ("\nenter name: ", stdout);
if (scanf ("%511[^\n]", name) != 1) { /* ALWAYS CHECK RETURN! */
fputs ("error: invalid input or user canceled.", stderr);
return 1;
}
empty_stdin(); /* empty any chars that remain in stdin */
insert_at_front (mylist, name); /* insert name */
fputs ("continue (y)/n: ", stdout); /* prompt to continue */
scanf ("%c", &i); /* read answer (or '\n' from pressing Enter) */
}
printf ("\nfinal list (%zu nodes):", mylist->size);
print_list (mylist);
free_list (mylist); /* don't forget to free memory you allocate */
return 0;
}
(note: the prompt to continue allows the user to simply press Enter to indicate he wants to continue entering names, any other character will exit. That is why (y) is shown as the default in the prompt -- can you explain why and how that works?)
Example Use/Output
$ ./bin/llinshead
enter name: Mickey Mouse
continue (y)/n:
enter name: Donald Duck
continue (y)/n:
enter name: Pluto (the dog)
continue (y)/n:
enter name: Minnie Mouse
continue (y)/n: n
final list (4 nodes): Minnie Mouse, Pluto (the dog), Donald Duck, Mickey Mouse
Memory Use/Error Check
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/llinshead
==5635== Memcheck, a memory error detector
==5635== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==5635== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==5635== Command: ./bin/llinshead
==5635==
enter name: Mickey Mouse
continue (y)/n:
enter name: Donald Duck
continue (y)/n:
enter name: Pluto (the dog)
continue (y)/n:
enter name: Minnie Mouse
continue (y)/n: n
final list (4 nodes): Minnie Mouse, Pluto (the dog), Donald Duck, Mickey Mouse
==5635==
==5635== HEAP SUMMARY:
==5635== in use at exit: 0 bytes in 0 blocks
==5635== total heap usage: 9 allocs, 9 frees, 134 bytes allocated
==5635==
==5635== All heap blocks were freed -- no leaks are possible
==5635==
==5635== For counts of detected and suppressed errors, rerun with: -v
==5635== 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 any further questions.
So I'm having a bit of trouble with Linked Lists in C. In general, I get the concept and and the algorithm since I have learned the ideas already in Java. But it seems like it's a different story in C, as we also take into consideration memory allocation.
Anyway, I have this code down here:
while (curr != NULL) {
printf("%s\n", (*curr).name);
curr = (*curr).next;
}
Where curr is a struct Student and one of the "attributes" of Student is name. So suppose I have already added in my nodes to the list. And when I execute the above, I seem to be getting the same names altogether. Below is my code for adding the nodes into the linked list:
void insertNode(char *name, int idNum, char sex) {
struct Student *s;
s = malloc(sizeof(struct Student));
//not entirely sure if this is the right way to do it
if (s == NULL) {
printf("Memory allocation failed.");
return;
}
(*s).name = name;
(*s).idNum = idNum;
(*s).sex = sex;
(*s).next = head; //head is the start of the list
head = s; //inserting the node at the beginning of the list
curr = head; //place the current pointer at the start of list
}
Basically it seems like I have no problems with the int and char. If I have {"Alice", 1000, 'F'} -> {"Bob", 1001, 'M'} -> {"Charlie", 1002, 'M'} in my list, I noticed the last name added to the list, Alice, will be the one that's being printed out. So the printout will be:
Alice
Alice
Alice
Am I doing something wrong here? Is there something I'm missing? I'm a beginner in C and still learning by myself. Thanks so much for any help and advice!
You need to keep track of the head pointer.
Here is the complete working code:
#include <stdio.h>
#include <stdlib.h>
struct Student
{
char * name;
int idNum;
char sex;
struct Student * next;
};
struct Student * head=NULL;
struct Student *
insertNode (char *name, int idNum, char sex)
{
struct Student *s;
s = malloc (sizeof (struct Student));
if (s == NULL)
{
printf ("Memory allocation failed.");
return NULL;
}
(*s).name = name;
(*s).idNum = idNum;
(*s).sex = sex;
(*s).next = head; //head is the start of the list
head = s;
return head;
}
int
main ()
{
insertNode("Alice", 1, 'F');
insertNode("Peter", 2, 'M');
insertNode("Mike", 3, 'M');
while (head != NULL)
{
printf ("%s\n", (*head).name);
head = (*head).next;
}
}
Output:
Mike
Peter
Alice
However, there are plenty of improvements that can still be done in your code. In particular, the notation (*s).name has a shorthand in C and that is s->name which is more clean. You can also avoid the global head variable and instead pass its pointer so that its changed value can be maintained in between calls.
(*s).name = name; sets the name in the struct to the local variable name which will become invalid at the end of the insertNode function.
You need to make a copy of the name.
You can use s->name = strdup(name); (Just rememeber you need to free s->name when you delete a node.
perhaps a simpler method whice you get your head around C would be to make name in the student node an array char name[32]; or similar. Then you would strcpy() into it but have one less thing to free.
Your question is a bit unclear as to the exact source of the problem. The answer by Syed does a good job identifying a solution, but there are some subtleties you need to be aware of as you are learning linked-lists.
First, your assignment of s->name = name; will only work if the names are string literals where you assign the address to the beginning of a string stored in read-only memory to s->name.
If you are reading values from a file (or stdin) and passing a pointer containing name to insertnode, then all of your nodes s->name will hold the pointer address, which, if it is still in scope will point to the storage containing the Last Name Read, and if your pointer used to pass name to insertnode has gone out of scope, you will invoke Undefined Behavior attempting to access s->name.
As correctly noted in the comments, the way to handle the situation is to allocate storage for each name passed to insertnode, assign the starting address to s->name and copy name to s->name. You can use strdup if you have that available, otherwise a simple allocation and strcpy is all that is required:
size_t len = strlen (name); /* get length of name */
/* allocate/validate storage for name */
if ((s->name = malloc (len + 1)) == NULL) {
perror ("malloc - name");
free (s);
return NULL;
}
strcpy (s->name, name); /* copy name to s->name */
(note: since strdup allocates memory (e.g. in s->name = strdup(name);), you should still validate s->name is not NULL after the call. Also note that strings is C are terminated with the nul-characer, '\0'. Therefore, in order to allocate enough storage to hold name plus the nul-terminating character, you must allocate strlen (name) + 1 bytes.)
Next, while 100% OK, your insert-node-before head in a singly-linked list will have the affect of reversing the order of the nodes inserted into your list. While this may not matter for your implementation, it may be surprising to you when you do print the list. This does save iterating to the next free node on insert, but sacrifices input order to do so.
An alternative, would be to insert the nodes in order by simply checking if head is NULL, and if so, insert the first node, otherwise, iterate until list->next = NULL and insert the new node as list->next. (this also requires that you initialize all s->next nodes NULL when you allocate storage for them. A simple implementation (making use of a typedef for struct student for convenience), would look similar to:
/* returns pointer to new node on success, or NULL on failure */
student *insertnode (student **head, char *name, int idnum, char sex)
{
student *s = malloc (sizeof *s); /* allocate new node */
if (s == NULL) { /* validate allocation */
perror ("malloc - s");
return NULL;
}
/* populate new node */
size_t len = strlen (name); /* get length of name */
/* allocate/validate storage for name */
if ((s->name = malloc (len + 1)) == NULL) {
perror ("malloc - name");
free (s);
return NULL;
}
strcpy (s->name, name); /* copy name to s->name */
// s->name = name; /* only works for string literals */
s->idnum = idnum;
s->sex = sex;
s->next = NULL; /* always initialize ->next NULL */
if (*head == NULL) { /* handle add 1st node */
*head = s;
}
else { /* handle add rest */
student *iter = *head; /* declare pointer to head */
while (iter->next != NULL) /* while ->next not null */
iter = iter->next; /* get next node */
iter->next = s; /* set iter->next to new node */
}
return s; /* head never changes, return current node
to indicate success/failure of insert. */
}
Also, you should avoid declaring global variables. There is no reason your list shouldn't be declared in main() and a pointer (or address of the pointer if the list address could change) be passed as a parameter to any function that needs to operate on the list. Just move the declaration of head inside main(), and then add a pointer to pointer to list as a parameter for insertnode (as was done above).
You should free() all memory you allocate. (yes, this happens at exit, but building good habits now of preserving a pointer to the beginning address of all memory you allocate, and freeing that memory when it is no longer needed will pay dividends and your project grow in size.
Finally, when you iterate over your list, you must use a separate pointer. Otherwise, if you iterate using head, it is a one-way street. When you iterate until head == NULL, you have lost the only reference you had to all the memory you allocated and have no way of getting them back. Simply use a temporary pointer, e.g.
student *iter = head; /* use separate pointer to iterate list */
while (iter != NULL) {
printf ("%-8s %4d %c\n", iter->name, iter->idnum, iter->sex);
iter = iter->next;
}
(of course on your final trip over the list to free all the list memory -- it doesn't matter what you use -- there won't be any more list when you are done)
Putting it altogether, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct student { /* typedef makes declarations easier */
struct student *next; /* pointers 1st limits size to 24-bytes */
char *name, sex;
int idnum;
} student;
/* returns pointer to new node on success, or NULL on failure */
student *insertnode (student **head, char *name, int idnum, char sex)
{
student *s = malloc (sizeof *s); /* allocate new node */
if (s == NULL) { /* validate allocation */
perror ("malloc - s");
return NULL;
}
/* populate new node */
size_t len = strlen (name); /* get length of name */
/* allocate/validate storage for name */
if ((s->name = malloc (len + 1)) == NULL) {
perror ("malloc - name");
free (s);
return NULL;
}
strcpy (s->name, name); /* copy name to s->name */
// s->name = name; /* only works for string literals */
s->idnum = idnum;
s->sex = sex;
s->next = NULL; /* always initialize ->next NULL */
if (*head == NULL) { /* handle add 1st node */
*head = s;
}
else { /* handle add rest */
student *iter = *head; /* declare pointer to head */
while (iter->next != NULL) /* while ->next not null */
iter = iter->next; /* get next node */
iter->next = s; /* set iter->next to new node */
}
return s; /* head never changes, return current node
to indicate success/failure of insert. */
}
int main (void) {
student *head = NULL;
insertnode (&head, "Alice", 1000, 'F'); /* insert nodes */
insertnode (&head, "Peter", 1001, 'M');
insertnode (&head, "Mike", 1002, 'M');
student *iter = head; /* use separate pointer to iterate list */
while (iter != NULL) {
printf ("%-8s %4d %c\n", iter->name, iter->idnum, iter->sex);
iter = iter->next;
}
/* free allocated memory */
while (head != NULL) { /* freeing list, pointer used doesn't matter */
student *victim = head; /* save pointer to node to delete */
head = head->next; /* move to next node */
free (victim->name); /* free storage for name */
free (victim); /* free storage for node */
}
}
(note: while not an error, the standard coding style for C avoids the use of camelCase or MixedCase variable names in favor of all lower-case while reserving upper-case names for use with macros and constants. It is a matter of style -- so it is completely up to you, but failing to follow it can lead to the wrong first impression in some circles.)
Example Use/Output
$ ./bin/ll_head_next
Alice 1000 F
Peter 1001 M
Mike 1002 M
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_head_next
==30229== Memcheck, a memory error detector
==30229== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==30229== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==30229== Command: ./bin/ll_head_next
==30229==
Alice 1000 F
Peter 1001 M
Mike 1002 M
==30229==
==30229== HEAP SUMMARY:
==30229== in use at exit: 0 bytes in 0 blocks
==30229== total heap usage: 6 allocs, 6 frees, 89 bytes allocated
==30229==
==30229== All heap blocks were freed -- no leaks are possible
==30229==
==30229== For counts of detected and suppressed errors, rerun with: -v
==30229== 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.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I made this program in c to reverse stack. but it is crashing. please help me figure out what is wrong. the program is working fine till taking inputs. but when reverse is called it crashes. i am not able to find the fault. all the memory is being allocated properly. so i dont think there is segmentation fault.
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
int data;
struct Node *next, *prev;
}SNode;
typedef struct{
SNode *top;
int count;
}Stack;
int isEmpty(Stack *s){
return (s->count==0);
}
void push(Stack *s,int x){
SNode *temp = (SNode *)malloc(sizeof(SNode));
temp->next = s->top;
temp->prev = NULL;
s->top->prev = temp;
temp->data = x;
s->top = temp;
s->count++;
}
int pop(Stack *s){
if(isEmpty(s)){
printf("Underflow");
return;
}
SNode *temp = s->top;
s->top = s->top->next;
s->top->prev = NULL;
int a = temp->data;
free(temp);
s->count--;
return a;
}
void reverse(Stack *s,Stack *rs){
while(!isEmpty(s)){
int p = pop(s);
push(rs,p);
}
}
int main(){
Stack *s = (Stack *)malloc(sizeof(Stack));
Stack *rs = (Stack *)malloc(sizeof(Stack));
char p='y';
while(p=='y'){
int pu;
printf("Enter data to be pushed: ");
scanf(" %d",&pu);
push(s,pu);
printf("Do you want to push another number? y/n:");
scanf(" %c",&p);
}
reverse(s,rs);
printf("Top of reversed stack: %d",rs->top->data);
return 0;
}
I dont know what i changed. i rewrote the code, this time using singly linked list, now it works really fine. dont know how !!??
#include<stdio.h>
#include<stdlib.h>
typedef struct node{
int data;
struct node *next;
}SNode;
typedef struct{
int count;
SNode *top;
}stack;
int isEmpty(stack *s){
return (s->count==0);
}
void push(stack *s,int x){
SNode *temp = (SNode *)malloc(sizeof(SNode));
temp->data = x;
temp->next = s->top;
s->top=temp;
s->count++;
}
int pop(stack *s){
if(isEmpty(s)){
printf("Underflow");
return -1;
}
SNode *temp = s->top;
s->top = temp->next;
int a = temp->data;
free(temp);
s->count--;
return a;
}
void reverseStack(stack *s, stack *rs){
while(!isEmpty(s)){
push(rs,pop(s));
}
}
int main(){
stack *s = (stack *)malloc(sizeof(stack));
stack *rs = (stack *)malloc(sizeof(stack));
s->count = rs->count =0;
char p ='y';
while(p=='y'){
int x;
printf("Enter data to push: ");
scanf("%d",&x);
push(s,x);
printf("Do you want to push more data? : y/n");
scanf(" %c",&p);
}
reverseStack(s,rs);
printf("Top of reversed stack: %d",rs->top->data);
return 0;
}
You have a number of problems in your implementation of your stack. The first of which is in push(), when you call:
temp->next = s->top;
s->top is an uninitialized pointer whose value is indeterminate. That invokes Undefined Behavior. Since this occurs on your very first call to push(s, pu), you can have zero confidence in the operation of the remainder of your code.
Next, both your push and pop function fail to test and handle in an appropriate manner whether the node being pushed or popped is the first or last node in the stack. These must be handled differently as the handling of your prev and next pointers will depend on whether other nodes exist. (you can't assign a next or prev node if you are pushing the first node, and you can't assign a new top on pop if no more nodes exist.
(note: you also seem to have used prev and next in a somewhat inconsistent manner. As long as you use them consistently, it doesn't matter whether you use prev for the existing top on push or next. I tend to have prev pointing down the stack and next pointing from the bottom up - but you are free to do it the other way)
To properly handle the first and last node in your stack, you can do a simple check on isempty() or simply check if (s->top == NULL) or if (s->count == 0).
You also need to initialize all pointers and count when you allocate storage for your stack and nodes. Failure to initialize all values on allocation will likely lead to an inadvertent attempted read from an uninitialized value (invoking additional instances of Undefined Behavior).
To eliminate that possibility, you can create simply helper functions, say create_node and create_stack to allocate, and validate the allocation, initialize the needed values, and then to provide a meaningful return indicating success or failure. You could do something simple like the following:
snode *create_node (int x)
{
snode *tmp = malloc (sizeof *tmp);
if (!tmp) {
perror ("create_node: memory exhausted.");
return NULL;
}
tmp->data = x;
tmp->prev = tmp->next = NULL;
return tmp;
}
stack *create_stack (void)
{
stack *tmp = malloc (sizeof *tmp);
if (!tmp) {
perror ("create_stack: memory exhausted.");
return NULL;
}
tmp->top = NULL;
tmp->count = 0;
return tmp;
}
Now there are no possibilities of an uninitialized value.
(Also note: while not an error, the standard coding style for C avoids the use of camelCase or MixedCase variable names in favor of all lower-case while reserving upper-case names for use with macros and constants. It is a matter of style -- so it is completely up to you, but failing to follow it can lead to the wrong first impression in some circles.)
With the helper functions defined, allocating and initializing your forward and reverse stacks in main() becomes a simple matter of:
int pu;
stack *s = create_stack();
stack *rs = create_stack();
if (!s || !rs) /* validate the return from create_stack */
return 1;
Having created your forward and reverse stack, you can then handle the first and last node cases in push() and pop() with the addition of a simple conditional as described above:
void push (stack * s, int x)
{
snode *temp = create_node (x);
if (!s->top) /* is the stack empty? */
s->top = temp;
else {
s->top->next = temp;
temp->prev = s->top;
s->top = temp;
}
s->count++;
}
and for pop(),
int pop (stack *s)
{
int x;
snode *temp;
if (isempty (s)) { /* checking empty as you did in original */
printf ("underflow");
return 0;
}
x = s->top->data;
temp = s->top;
if (s->top->prev)
s->top = s->top->prev;
s->top->next = NULL;
free (temp);
s->count--;
return x;
}
Adding a short prn_stack() function to iterate over the stack without poping and freeing the nodes, you could put together a short example program to test the push, pop and reverse as follows:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *next, *prev;
} snode;
typedef struct {
snode *top;
int count;
} stack;
snode *create_node (int x)
{
snode *tmp = malloc (sizeof *tmp);
if (!tmp) {
perror ("create_node: memory exhausted.");
return NULL;
}
tmp->data = x;
tmp->prev = tmp->next = NULL;
return tmp;
}
stack *create_stack (void)
{
stack *tmp = malloc (sizeof *tmp);
if (!tmp) {
perror ("create_stack: memory exhausted.");
return NULL;
}
tmp->top = NULL;
tmp->count = 0;
return tmp;
}
int isempty (stack *s)
{
return (s->count == 0);
}
void prn_stack (stack *s)
{
snode *iter;
if (isempty(s)) {
puts ("stack empty");
return;
}
iter = s->top;
for (; iter; iter = iter->prev)
printf (" %d\n", iter->data);
}
void push (stack * s, int x)
{
snode *temp = create_node (x);
if (!s->top)
s->top = temp;
else {
s->top->next = temp;
temp->prev = s->top;
s->top = temp;
}
s->count++;
}
int pop (stack *s)
{
int x;
snode *temp;
if (isempty (s)) {
printf ("underflow");
return 0;
}
x = s->top->data;
temp = s->top;
if (s->top->prev)
s->top = s->top->prev;
s->top->next = NULL;
free (temp);
s->count--;
return x;
}
void reverse (stack * s, stack * rs)
{
while (!isempty (s))
push (rs, pop (s));
}
int main ()
{
int pu;
stack *s = create_stack();
stack *rs = create_stack();
if (!s || !rs)
return 1;
while (scanf (" %d", &pu) == 1)
push (s, pu);
printf ("stack:\n");
prn_stack (s);
reverse (s, rs);
printf ("\nreversed stack:\n");
while (!isempty (rs))
printf (" %d\n", pop (rs));
free (s);
free (rs);
return 0;
}
(note: do not forget to free the memory you allocate for your forward and your reverse stacks after all the values have been popped, and always validate your memory use by using a memory-error checking program like valgrind on Linux. There are similar programs for all OS's)
Example Use/Output
$ echo "10 9 8 7 6 5 4 3 2 1" | ./bin/stack_rev
stack:
1
2
3
4
5
6
7
8
9
10
reversed stack:
10
9
8
7
6
5
4
3
2
1
To validate your memory use and that you have freed all memory you allocate and that there are no memory errors, just run your code through the memory checker, similar to the following using valgrind, e.g.
Memory Use/Error Check
$ echo "10 9 8 7 6 5 4 3 2 1" | valgrind ./bin/stack_rev
==18418== Memcheck, a memory error detector
==18418== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==18418== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==18418== Command: ./bin/stack_rev
==18418==
stack:
1
2
3
4
5
6
7
8
9
10
reversed stack:
10
9
8
7
6
5
4
3
2
1
==18418==
==18418== HEAP SUMMARY:
==18418== in use at exit: 0 bytes in 0 blocks
==18418== total heap usage: 22 allocs, 22 frees, 512 bytes allocated
==18418==
==18418== All heap blocks were freed -- no leaks are possible
==18418==
==18418== For counts of detected and suppressed errors, rerun with: -v
==18418== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Look things over and let me know if you have any further questions.
malloc doesn't initialize the struct members to zero and NULL. You have to do that yourself or use calloc instead. So change your malloc lines in your program to something like this:
Stack *s = calloc(1, sizeof(Stack));
Stack *rs = calloc(1, sizeof(Stack));
I see at least two problems.
When you malloc() a new Stack structure, you don't initialize the *top and count fields, and their contents is likely to be garbage.
In pop():
SNode *temp = s->top;
s->top = s->top->next;
s->top->prev = NULL;
What happens if the initial value of s->top->next is NULL?
Your code crashes randomly because s->count is not 0 when you first declare it in main. If you're lucky, s->count is 0, then your reverse is fine because isEmpty() will work correctly. Otherwise, you will get segmentation fault.
I'm relatively new to programming and I am having some issues passing my struct to other functions. Here is what my actual code looks like:
typedef struct Memcheck {
char *memoryAdd;
char *file;
int line;
struct Memcheck_struct *next;
} Memcheck;
char *strdup2( char *str )
{
char *new;
new = malloc( strlen(str)+1 );
if (new)
strcpy( new, str );
return new;
}
/*Allocate memory for a ptr, and add it to the top of the linked list*/
void *memcheck_malloc(size_t size, char *file, int line){
Memcheck * new_memoryCheck = NULL;
Memcheck * head = NULL;
head = malloc(sizeof(Memcheck));
new_memoryCheck = malloc(sizeof(Memcheck));
new_memoryCheck->memoryAdd = malloc(sizeof(new_memoryCheck->memoryAdd));
new_memoryCheck->file = malloc(sizeof(new_memoryCheck->file));
new_memoryCheck->file = strdup2(file);
new_memoryCheck->line = line;
new_memoryCheck->next = head;
return new_memoryCheck;
}
/*Prints the error messages*/
void printList(Memcheck *new_memoryCheck) {
Memcheck * head = NULL;
Memcheck * current = head;
head = malloc(sizeof(Memcheck));
current = malloc(sizeof(Memcheck));
printf("new_mem file: %s\n", new_memoryCheck->file);
printf("current file: %s\n", current->file);
while (current != NULL) {
printf("in loop\n");
printf("memcheck error: memory address %p which was allocated in file \"%s\", line %d, was never freed\n", current, current->file, current->line);
current = current->next;
}
}
int memcheck_main(Memcheck new_memoryCheck){
printf("newmem file: %s\n", new_memoryCheck.file);
printf("Entering printList\n");
printList(&new_memoryCheck);
return 0;
}
I have strdup2 because apparently ansi doesn't have stdrup.
I know to use pass by reference to some degree but I'm not exactly sure where to use the * and & operators
Since it appears that you are writing a surrogate for malloc() that records which memory was allocated where, you probably need code similar to:
typedef struct Memcheck Memcheck;
struct Memcheck
{
void *data;
size_t size;
const char *file;
int line;
Memcheck *next;
};
static Memcheck *memcheck_list = 0;
/* Allocate memory and record the allocation in the linked list */
void *memcheck_malloc(size_t size, const char *file, int line)
{
Memcheck *node = malloc(sizeof(*node));
void *data = malloc(size);
if (node == 0 || data == 0)
{
free(node);
free(data);
return 0;
}
node->data = data;
node->size = size;
node->file = file;
node->line = line;
node->next = memcheck_list;
memcheck_list = node;
return data;
}
Note that if either (or both) memory allocations fails, the memory is all freed before returning. Using free() on a null (0) pointer is a no-op. Thus the clean-up is safe. The information can simply be copied into the structure as shown; no need for extra memory allocations for the file name, for example, as long as you pass __FILE__ to the function (which is a string literal, and therefore has a lifetime as long as the rest of the program).
#define MAXL 256
First pass: in = "25 7 * 14 - 6 +"; run smoothly with correct answer.
Second pass: in = "1 24 3 + * 41 -"; program stopped right after outputting Num got in: 41 Expected: continue loop and get in the minus sign then pop(s) and subtract 41
I'm guessing that my program ran out of allocated space because of the free() didn't do their job as I expected but I'm not sure.
Any help would be greatly appreciated. Thank you!
double evaluatePost(char * in)
{
double * op1 = NULL, * op2 = NULL, * msgr = NULL;
int i, j;
char * c = {0}, tempExp[MAXL] = {0};
char ** token = NULL;
Stack * s = createStack();
strcpy(tempExp, in); /* Copy in to a temporary array so strtok will not destroy in */
for(c = strtok(tempExp, " "); c != NULL; ++i, c = strtok(NULL, " "))
{
if(isdigit(c[0]))
{
printf("\nNum got in: %s\n", c); /* Crash right after this line output 41 */
system("PAUSE");
msgr = (double*)malloc(sizeof(double)); /* I made a malloc check here, it never showed error */
*msgr = atoi(c); /* I don't know if it crash at this line or the next one */
push(s, msgr); /* stack has no limit, receives stack* and void* */
/* It never got pass to here after output 41 */
}
else
{
op2 = (double *)pop(s);
op1 = (double *)pop(s);
printf("\n%f %f %s\n", *op1, *op2, c);
system("PAUSE");
msgr = (double*)malloc(sizeof(double));
if(!msgr)
{
printf("Memory allocation failed.\n");
system("PAUSE");
exit(1);
}
switch(*c)
{
case '+': *msgr = (*op1 + *op2); break;
case '-': *msgr = (*op1 - *op2); break;
case '*': *msgr = (*op1 * *op2); break;
case '/': *msgr = (*op1 / *op2); break;
}
printf("\n%.1f\n", *msgr);
system("PAUSE");
/* Free the memory before they become orphans */
free(op1), free(op2);
push(s, msgr);
}
}
returnVal = *((double *)pop(s));
makeEmpty(s);
return returnVal;
}
void push(Stack * stack, void * dataInPtr)
{
/* Define a new StackNode */
StackNode * newPtr;
/* Get some Memory */
newPtr = (StackNode*)malloc(sizeof(StackNode));
if(!newPtr)
{
printf("Out of memory");
system("PAUSE");
exit(1);
}
/* Assign dataIn to dataPtr */
newPtr->dataPtr = dataInPtr;
/* Make the links */
newPtr->link = stack->top; /* Point both to top */
stack->top = newPtr; /* newPtr at top pointed to be head */
(stack->count)++;
}
void * pop(Stack * stack)
{
/* Hold the data */
void * dataOutPtr;
StackNode * temp;
/* Check if stack is empty */
if(stack->count == 0)
dataOutPtr = NULL;
else
{
/* Get the data and remove the node */
temp = stack->top; /* temp points to top */
dataOutPtr = stack->top->dataPtr; /* dataOutPtr has data */
stack->top = stack->top->link; /* stack moves to next node */
temp->link = NULL; /* break top node off stack */
free(temp); /* frees memory */
(stack->count)--;
}
return dataOutPtr;
}
typedef struct node
{
void * dataPtr;
struct Node * link;
} StackNode;
typedef struct
{
int count;
StackNode * top;
} Stack;
Your problem may lie here:
You are freeing the Stack node in pop()
temp = stack->top; /* temp points to top */
dataOutPtr = stack->top->dataPtr; /* dataOutPtr has data */
stack->top = stack->top->link; /* stack moves to next node */
temp->link = NULL; /* break top node off stack */
free(temp); /* frees memory */
And after that you are freeing the data pointer in the node in evaluatePost():
free(op1), free(op2);
The sequence freeing the memory should always be in reverse order of malloc i.e. you should allocate Node first and then node data and for freeing free the node data first, and then Node itself.
You are facing problem with some input and not with some other input, because this is undefined behavior.
To know more about undefined behavior in C, you can search for related questions in SO.
You can easily add the ability to traverse over all the nodes in your stack without freeing them. This comes in handy, if say, after pushing all nodes you need to use the collection and don't want to pop and free the data, or if you need to update the nodes in some way before popping them. All you need to do is add a single tail pointer to your stack which will hold the address of the first node pushed onto the stack. e.g.:
typedef struct
{
int count;
StackNode *top;
StackNode *tail;
} Stack;
Why? Since you have a StackNode->link, your data is effectively in linked-list form anyway. Adding a tail pointer that points to the first node pushed onto the stack, simply gives an easy way to know when to stop iterating over your nodes. With a few minor tweaks, you can then do what you attempted - iterate over all the nodes in your stack without having to pop/free the nodes as you do it.
Here is a short examples showing the minor tweaks needed. Also notice the cleanup removes all casts of malloc (don't do it) and a couple of cleanups on the malloc calls themselves:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
void *dataPtr;
struct node *link;
} StackNode;
typedef struct
{
int count;
StackNode *top;
StackNode *tail;
} Stack;
Stack *stackcreate ()
{
Stack *s = malloc (sizeof *s);
if (!s) {
fprintf (stderr, "%s() error: memory allocation failed.\n", __func__);
exit (EXIT_FAILURE);
}
s->count = 0;
s->top = s->tail = NULL;
return s;
}
void push(Stack * stack, void * dataInPtr)
{
if (!stack) return;
/* Define a new StackNode & allocate */
StackNode *newPtr = malloc (sizeof *newPtr);
if(!newPtr)
{
fprintf (stderr, "%s() error: memory allocation failed.\n", __func__);
exit (EXIT_FAILURE);
}
/* Assign dataIn to dataPtr */
newPtr->dataPtr = dataInPtr;
newPtr->link = stack->top; /* Point both to top */
/* Make the links */
if (!stack->top)
stack->tail = newPtr;
stack->top = newPtr; /* newPtr at top pointed to be head */
(stack->count)++;
}
void * pop(Stack * stack)
{
/* Hold the data */
void * dataOutPtr;
StackNode * temp;
/* Check if stack is empty */
if(stack->count == 0)
dataOutPtr = NULL;
else
{
/* Get the data and remove the node */
temp = stack->top; /* temp points to top */
dataOutPtr = stack->top->dataPtr; /* dataOutPtr has data */
stack->top = stack->top->link; /* stack moves to next node */
temp->link = NULL; /* break top node off stack */
free(temp); /* frees memory */
(stack->count)--;
if (!stack->top) stack->tail = NULL;
}
return dataOutPtr;
}
int main (void) {
char *lines[] = { "my cat has more...",
"has lots of fleas, ",
"my dog, the lab, " };
size_t i = 0;
size_t entries = sizeof lines/sizeof *lines;
Stack *stack = stackcreate ();
for (i = 0; i < entries; i++)
push (stack, (void *)lines[i]);
printf (" \niterating over nodes in stack without popping\n\n");
StackNode *p;
for (p = stack->top; ;p = p->link) {
printf (" %s\n", (char *)p->dataPtr);
if (p == stack->tail)
break;
}
printf ("\n popping items from stack\n\n");
while (stack->top) {
printf (" %s\n", (char *)pop (stack));
}
printf ("\n");
return 0;
}
Example/Output
$ ./bin/stack_iter
iterating over nodes in stack without popping
my dog, the lab,
has lots of fleas,
my cat has more...
popping items from stack
my dog, the lab,
has lots of fleas,
my cat has more...