#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...
Related
I'm trying to make a shop system. I have a linked list and a stack. The linked list store the Person information (id and name) and the Stack store the Products (product).
I need to link both of them but I don't know how. I want to create some Person and the person will choose some products like:
Person1:
Apple
Orange
Person2:
Strawberry
Lemon
Every person will have their chosen products.
Here's my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define TAM 30
typedef int ID;
typedef char PRODUCT[TAM];
typedef struct stack{
int nItems;
int capacity;
PRODUCT *product;
} Stack;
typedef struct person{
Stack *stack;
ID id;
char name[TAM];
struct person *next;
}Person;
typedef struct list{
Person *head;
Person *end;
}List;
void createList(List *list){
list->head = NULL;
list->end = NULL;
}
void insert(List *list, Person person){
Person *newPerson = (Person*)malloc(sizeof(Person));
Person *ant = NULL;
Person *a = list->head;
strcpy(newPerson->name, person.name);
newPerson->id = person.id;
while(a != NULL && a->id < a->id){
ant = a;
a = a->next;
}
if (ant == NULL){
newPerson->next = list->head;
list->head = newPerson;
}else{
newPerson->next = ant->next;
ant->next = newPerson;
}
}
void printList(List *list){
Person *a;
a = list->head;
while(a != NULL){
printf("ID: %d | Name: %s\n", a->id, a->name);
a = a->next;
}
}
Stack *createStack(int capacity){
Stack *stack = malloc(sizeof(Stack));
stack->product = malloc(sizeof(int) * capacity);
stack->capacity = capacity;
return stack;
};
bool push(Stack *stack, PRODUCT product){
if (stack->capacity == stack->nItems){
return false;
}
strcpy(stack->product[stack->nItems], product);
return true;
}
void printStack(Stack *stack){
int i;
for(i = 0; i < stack->nItems; i++){
printf("product: %s\n", stack->product[stack->nItems - i - 1]);
}
}
int main(){
int capacity = 10;
Stack *stack = createStack(10);
List list;
Person p1 = {1, "James"};
PRODUCT prod1 = {"Apple"};
createList(&list);
insert(&list, p1);
push(stack, prod1);
printList(&list);
printStack(stack);
return 0;
}
You can do exactly what you are attempting to do, but you have missed the point of Person including Stack *stack; by creating a separate Person and Stack in main(). There is no relationship between the two if you create a separate Person and a separate Stack. Because Person includes a pointer to stack, you must create the stack when you insert() the person.
You are thinking along the correct lines in how you approach the list and stack, you are just struggling to get the pieces put together correctly and in the right order.
Let's start with your attempt in main() to create p1 which you want to use as a temporary struct to insert. You declare and initialize Person p1 = {1, "James"};, but the first member in Person is Stack *stack;. So, of course, the compiler chokes when it sees 1 as the initializer for Stack *stack;. You can make your wanted initialization work by moving Stack *stack; to the 3rd member in the struct, e.g.
typedef struct person {
ID id;
char name[TAM];
Stack *stack; /* move to 3rd member */
struct person *next;
} Person;
When you allocate memory, you must validate EVERY allocation. If there are multiple allocations in any one function, if the 2nd (or later) allocation fails, you must clean up (free()) the prior allocations before returning an error condition. You can see that in createStack(), e.g.
Stack *createStack (int capacity)
{
Stack *stack = malloc (sizeof *stack);
if (!stack) { /* validate EVERY allocation */
perror ("malloc-stack");
return NULL;
}
stack->product = malloc (capacity * sizeof *stack->product);
if (!stack->product) { /* validate EVERY allocation */
perror ("malloc-stack->product");
free (stack); /* free stack memory on failure */
return NULL;
}
stack->capacity = capacity;
stack->nItems = 0; /* don't forget to initialize nItems 0 */
return stack;
}
As mentioned above, since the Stack *stack; is a member of Person, you must create the stack as part of creating the Person. (you can do it in separate functions if you like, but you create Person->stack NOT some separate unrelated stack). It is common to do it in the same function where you create your Person, e.g.
Person *insert (List *list, const Person *person, int capacity)
{
Person *newPerson = malloc (sizeof *newPerson);
if (!newPerson) { /* validate EVERY allocation */
perror ("malloc-newPerson");
return NULL;
}
*newPerson = *person; /* assign automatic struct members */
/* create / validate stack */
newPerson->stack = createStack (capacity);
if (!newPerson->stack) {
free (newPerson); /* free newPerson memory on failure */
return NULL;
}
newPerson->next = NULL; /* initialize pointers NULL */
if (!list->head) { /* good job on the end ptr, now use it */
list->head = list->end = newPerson;
}
else {
list->end->next = newPerson;
list->end = newPerson;
}
return newPerson;
}
(NOTE: insert cannot be type void. You must return a type that indicates whether your allocation within the function succeeded or failed. Simply returning the pointer you create in the function (or NULL on failure) works fine.)
Your push() function should provide a meaningful error if the stack is full, e.g.
bool push (Stack *stack, const PRODUCT product)
{
if (stack->capacity == stack->nItems) {
/* provide meaningful error messages */
fprintf (stderr, "error: failed to add: %s (stack full)\n",
product);
return false;
}
strcpy (stack->product[stack->nItems], product);
stack->nItems++; /* don't forget to increment nItems */
return true;
}
(note: the addition of the increment of stack->nItems, without it you just keep overwriting the first product...)
Since your stack is part of Person, you need to printStack() as part of printList(), e.g.
void printStack (Stack *stack)
{
int i;
if (!stack->nItems) { /* don't forget to handle empty-stack */
puts ("stack-empty"); /* or your printf() ivokes UB */
return;
}
for (i = 0; i < stack->nItems; i++) {
printf (" product: %s\n", stack->product[stack->nItems - i - 1]);
}
}
void printList (List *list){
Person *a = list->head;
while (a != NULL) {
printf ("\nID: %d | Name: %s\n", a->id, a->name);
printStack (a->stack);
a = a->next;
}
}
(note: the addition of the validation in printStack() to avoid Undefined Behavior by referencing a negative index if the stack is empty)
(also note: there is no reason to print the stack in reverse order -- unless you just want to. You are adding the product at stack->nItems each time so they will be in the order added)
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. You do fine with (1), but completely fail doing (2).
Yes, since your list is created in main(), there is no technical memory-leak since the memory will be freed on program exit, but you will not always be creating lists in main(). In that case, if you fail to free() before leaving the function you created in -> memory-leak. Build good habits early, always track and free what you allocate. A simple routine to free your stack and Person nodes in your list could be:
void del_stack (Stack *stack)
{
free (stack->product);
free (stack);
}
void del_list (List *list)
{
Person *person = list->head;
while (person) {
Person *victim = person;
del_stack (victim->stack);
person = person->next;
free (victim);
}
}
Now you can simply call del_list (&list); to free all allocated memory.
Lastly, be very careful trying to be too creative with typedef. Never typedef pointers and make sure you understand a typedef of arrays. In your code you have:
typedef char PRODUCT[TAM];
and then in Stack you have:
PRODUCT *product;
What is product? Is it a pointer-to-array or is it an array-of-pointers? (are you sure?) (hint: you have single-allocation, single-free, so it can only be one of the two)
Now in main(), it is fine to use a temporary pointer to Person to initialize and then insert in your list. But don't confuse the temporary Person with the node created in the list. Putting all the above together, and intentionally attempting to insert 12 products where only 10 will fit to exercise the checks in the code, you could do:
int main() {
int capacity = 10;
List list;
PRODUCT prod = "";
createList (&list);
Person tmp = { .id = 1, .name = "James"};
/* must create stack with person */
Person *p1 = insert (&list, &tmp, capacity);
if (!p1) { /* validate node & stack creation succeeds */
return 1;
}
for (int i = 0; i < capacity + 2; i++) {
sprintf (prod, "Product_%d", i + 1);
if (!push (p1->stack, prod)) {
break;
}
}
printList (&list);
del_list (&list); /* don't forget to free what you allocate */
}
Example Use/Output
$ ./bin/llstack
error: failed to add: Product_11 (stack full)
ID: 1 | Name: James
product: Product_10
product: Product_9
product: Product_8
product: Product_7
product: Product_6
product: Product_5
product: Product_4
product: Product_3
product: Product_2
product: Product_1
Memory Use/Error Check
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/llstack
==7287== Memcheck, a memory error detector
==7287== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7287== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==7287== Command: ./bin/llstack
==7287==
error: failed to add: Product_11 (stack full)
ID: 1 | Name: James
product: Product_10
product: Product_9
product: Product_8
product: Product_7
product: Product_6
product: Product_5
product: Product_4
product: Product_3
product: Product_2
product: Product_1
==7287==
==7287== HEAP SUMMARY:
==7287== in use at exit: 0 bytes in 0 blocks
==7287== total heap usage: 4 allocs, 4 frees, 1,396 bytes allocated
==7287==
==7287== All heap blocks were freed -- no leaks are possible
==7287==
==7287== For lists of detected and suppressed errors, rerun with: -s
==7287== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Complete Example Code
For easy reference, here are the complete modifications to your code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define TAM 30
typedef int ID;
typedef char PRODUCT[TAM];
typedef struct stack {
int nItems;
int capacity;
PRODUCT *product;
} Stack;
typedef struct person {
ID id;
char name[TAM];
Stack *stack; /* move to 3rd member */
struct person *next;
} Person;
typedef struct list {
Person *head;
Person *end;
} List;
void createList (List *list)
{
list->head = NULL;
list->end = NULL;
}
Stack *createStack (int capacity)
{
Stack *stack = malloc (sizeof *stack);
if (!stack) { /* validate EVERY allocation */
perror ("malloc-stack");
return NULL;
}
stack->product = malloc (capacity * sizeof *stack->product);
if (!stack->product) { /* validate EVERY allocation */
perror ("malloc-stack->product");
free (stack); /* free stack memory on failure */
return NULL;
}
stack->capacity = capacity;
stack->nItems = 0; /* don't forget to initialize nItems 0 */
return stack;
}
Person *insert (List *list, const Person *person, int capacity)
{
Person *newPerson = malloc (sizeof *newPerson);
if (!newPerson) { /* validate EVERY allocation */
perror ("malloc-newPerson");
return NULL;
}
*newPerson = *person; /* assign automatic struct members */
/* create / validate stack */
newPerson->stack = createStack (capacity);
if (!newPerson->stack) {
free (newPerson); /* free newPerson memory on failure */
return NULL;
}
newPerson->next = NULL; /* initialize pointers NULL */
if (!list->head) {
list->head = list->end = newPerson;
}
else {
list->end->next = newPerson;
list->end = newPerson;
}
return newPerson;
}
bool push (Stack *stack, const PRODUCT product)
{
if (stack->capacity == stack->nItems) {
/* provide meaningful error messages */
fprintf (stderr, "error: failed to add: %s (stack full)\n",
product);
return false;
}
strcpy (stack->product[stack->nItems], product);
stack->nItems++; /* don't forget to increment nItems */
return true;
}
void printStack (Stack *stack)
{
int i;
if (!stack->nItems) { /* don't forget to handle empty-stack */
puts ("stack-empty"); /* or your printf() ivokes UB */
return;
}
for (i = 0; i < stack->nItems; i++) {
printf (" product: %s\n", stack->product[stack->nItems - i - 1]);
}
}
void printList (List *list){
Person *a = list->head;
while (a != NULL) {
printf ("\nID: %d | Name: %s\n", a->id, a->name);
printStack (a->stack);
a = a->next;
}
}
void del_stack (Stack *stack)
{
free (stack->product);
free (stack);
}
void del_list (List *list)
{
Person *person = list->head;
while (person) {
Person *victim = person;
del_stack (victim->stack);
person = person->next;
free (victim);
}
}
int main() {
int capacity = 10;
List list;
PRODUCT prod = "";
createList (&list);
Person tmp = { .id = 1, .name = "James"};
/* must create stack with person */
Person *p1 = insert (&list, &tmp, capacity);
if (!p1) { /* validate node & stack creation succeeds */
return 1;
}
for (int i = 0; i < capacity + 2; i++) {
sprintf (prod, "Product_%d", i + 1);
if (!push (p1->stack, prod)) {
break;
}
}
printList (&list);
del_list (&list); /* don't forget to free what you allocate */
}
Let me know if you have further questions.
I have the following program which reads words from a text file and creates a single linked list, with each node containing: word, count, next.
When a word already exists in the linked list the count is updated, otherwise, a node is created at the end of the linked list.
All of my functions work, except for the one where I am adding a word to the end of the linked list. There is likely an error with linkage of the nodes?
with my following text file: line1 "my name is natalie", line 2 "my dog is niko"
I should be getting the following output: my(2), name(1), is(2), natalie(1), dog(1), niko(1)
but I am getting: my(2), dog(2), s(1), iko(1), is(1), niko(1)
WHERE IS MY ERROR?
//function to add word to linked list
struct WordNode *addWord(char* word, struct WordNode *wordListHead){
//create new node
struct WordNode *current = malloc(sizeof(struct WordNode));
current->word = word;
current->count = 1;
current->next = NULL;
//
while(1){
if((wordListHead != NULL)&&(wordListHead->next ==NULL)){
//connect
wordListHead->next=current;
break;
}
wordListHead = wordListHead->next;
}
}
called here in main:
char *filename = argv[1];
FILE *fp = fopen(filename, "r");
printf("%s\n", filename);
if (fp == NULL){
printf("Error: unable to open the file ");
printf("%s", filename);
return 1;
}
else {
char *delim = " \t\n,:;'\".?!#$-><(){}[]|\\/*&^%#!~+=_"; // These are our word delimiters.
char line[1000];
char * token;
int count = 0;//count used so that first word of the doc can be created as head node
//create head pointer
struct WordNode *wordListHead = NULL;
//create current pointer
struct WordNode *current = NULL;
//iterate through each line in file
while(fgets(line, 1000, fp)){
//seperate each word
//first word of line
token = strtok(line, delim);
printf("%s\n", token);
if(count == 0){
//first word of document, create first wordnode (head node)
wordListHead = malloc(sizeof(struct WordNode));
wordListHead->word = token;
wordListHead->count = 1;
wordListHead->next = NULL;
}
else{
//check if first word of line exists in linked list
if((doesWordExist(token, wordListHead)) == 0){
//update count
updateCount(token, wordListHead);
}
else{
//create node
addWord(token, wordListHead);
}
}
//iterate through all the other words of line
while ((token=strtok(NULL, delim)) != NULL){
printf("%s\n", token);
//check if name is in linked list
if((doesWordExist(token, wordListHead)) == 0){
//update count
updateCount(token, wordListHead);
}
else{
//create node
addWord(token, wordListHead);
}
}
count++;
}
printWordList(wordListHead);
}
}
struct defined here:
//structure definition of linked list node
#ifndef WORDLISTH
#define WORDLISTH
struct WordNode{
char *word;
unsigned long count;
struct WordNode *next;
};
void printWordList( struct WordNode *wordListHead);
struct WordNode *addWord(char* word , struct WordNode *wordListead);
#endif
other functions for reference:
//function to check if word is in linked list
bool doesWordExist(char* myword, struct WordNode *wordListHead){
while (wordListHead != NULL){
if(strcmp(wordListHead->word, myword) == 0){
return 0;
}
wordListHead= wordListHead-> next;
}
return 1;
}
//function to update the count of word
void updateCount(char* myword, struct WordNode *wordListHead){
while (wordListHead != NULL){
if(strcmp(wordListHead->word, myword) == 0){
//update count value
//capture current count and add 1
int curcount = wordListHead->count;
int newcount = curcount + 1;
wordListHead->count = newcount;
//printf("%d\n", newcount);
}
wordListHead= wordListHead-> next;
}
}
//function to print word list
//takes head node as argument
void printWordList( struct WordNode *wordListHead){
//WordNode *toyptr = wordListHead;
while (wordListHead != NULL){
printf("%s\n", wordListHead->word);
printf("%ld\n", wordListHead->count);
wordListHead = wordListHead -> next;
}
}
When you are storing token into your struct, you are using a pointer that is part of the input buffer.
On a new input line, the tokens gathered from previous lines will be corrupted/trashed.
You need to allocate space to store the token in the struct on the heap. Use strdup for that.
So, in addWord, you want:
current->word = strdup(word);
And in main, you want:
wordListHead->word = strdup(token);
UPDATE:
That's the primary issue. But, your code does a bunch of needless replication.
addWord doesn't handle an empty list. But, if it did there would be no need for main to have separate [replicated] code for the first word and subsequent words on the line.
The strcmp can be incorporated into addWord and it can "do it all". (i.e. a single scan of the list)
For doesWordExist, it returns a bool on a match. If it returned the pointer to the element that matched, updateCount would just have to increment the count [and not rescan the list]. I've updated those functions accordingly, but they are no longer needed due to the changes to addWord
Here's how I would simplify and refactor the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef int bool;
#ifdef DEBUG
#define dbgprt(_fmt...) \
printf(_fmt)
#else
#define dbgprt(_fmt...) do { } while (0)
#endif
//structure definition of linked list node
#ifndef WORDLISTH
#define WORDLISTH
struct WordNode {
char *word;
unsigned long count;
struct WordNode *next;
};
void printWordList(struct WordNode *wordListHead);
#endif
//function to add word to linked list
struct WordNode *
addWord(char *word, struct WordNode **list)
{
struct WordNode *curr;
struct WordNode *prev = NULL;
struct WordNode *newnode = NULL;
for (curr = *list; curr != NULL; curr = curr->next) {
if (strcmp(curr->word,word) == 0) {
newnode = curr;
break;
}
prev = curr;
}
// create new node
do {
// word already exists
if (newnode != NULL)
break;
newnode = malloc(sizeof(struct WordNode));
newnode->word = strdup(word);
newnode->count = 0;
newnode->next = NULL;
// attach to tail of list
if (prev != NULL) {
prev->next = newnode;
break;
}
// first node -- set list pointer
*list = newnode;
} while (0);
// increment the count
newnode->count += 1;
return newnode;
}
//function to check if word is in linked list
struct WordNode *
findWord(char *myword, struct WordNode *head)
{
struct WordNode *curr;
for (curr = head; curr != NULL; curr = curr->next) {
if (strcmp(curr->word,myword) == 0)
break;
}
return curr;
}
//function to update the count of word
void
updateCount(char *myword, struct WordNode *head)
{
struct WordNode *match;
match = findWord(myword,head);
if (match != NULL)
match->count += 1;
}
//function to print word list
//takes head node as argument
void
printWordList(struct WordNode *head)
{
struct WordNode *curr;
for (curr = head; curr != NULL; curr = curr->next) {
printf("%s", curr->word);
printf(" %ld\n", curr->count);
}
}
int
main(int argc, char **argv)
{
char *filename = argv[1];
FILE *fp = fopen(filename, "r");
printf("FILE: %s\n", filename);
if (fp == NULL) {
printf("Error: unable to open the file ");
printf("%s", filename);
return 1;
}
// These are our word delimiters.
char *delim = " \t\n,:;'\".?!#$-><(){}[]|\\/*&^%#!~+=_";
char line[1000];
char *token;
// create head pointer
struct WordNode *wordListHead = NULL;
// iterate through each line in file
while (fgets(line, sizeof(line), fp)) {
// seperate each word
// first word of line
char *bp = line;
while (1) {
token = strtok(bp, delim);
bp = NULL;
if (token == NULL)
break;
dbgprt("TOKEN1: %s\n", token);
addWord(token,&wordListHead);
}
}
printWordList(wordListHead);
return 0;
}
UPDATE #2:
Note that addWord and findWord replicate code. The first part of addWord is [essentially] duplicating what findWord does.
But, addWord can not just use findWord [which would be desirable] because findWord, if it fails to find a match returns NULL. In that case, it doesn't [have a way to] communicate back the last element (i.e. the "tail" of the list) which addWord needs to append to.
While we could add an extra argument to findWord to propagate this value back, a better solution is to create a different struct that defines a "list".
In the existing code, we are using a "double star" pointer to the head word node as a "list". Using a separate struct is cleaner and has some additional advantages.
We can just pass around a simple pointer to the list. We no longer need to worry about whether we should be passing a double star pointer or not.
Although we're only using a singly linked list, a separate list struct helps should we wish to convert the list to a doubly linked list [later on].
We just pass around a pointer to the list, and the list can keep track of the head of the list, the tail of the list, and the count of the number of elements in the list.
Linked lists lend themselves well to sorting with mergesort. The list count helps make that more efficient because it is much easier to find the "midpoint" of the list [which a mergesort would need to know].
To show the beginnings of the doubly linked list, I've added a prev element to the word struct. This isn't currently used, but it hints at the doubly linked version.
I've reworked all functions to take a pointer to a list, rather than a pointer to the head node.
Because the list struct has a tail pointer, addWord can now call findWord. If findWord does not find a match, addWord can simply use the head/tail pointers in the list to find the correct insertion point.
To simplify a bit more, I've changed the word node [and the list struct] to use some typedef statements.
Also, it's more usual/idiomatic to have the dominant struct pointer be the first argument, so I've reversed the order of the arguments on some of the functions.
Anyway, here's the [further] refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef int bool;
#ifdef DEBUG
#define dbgprt(_fmt...) \
printf(_fmt)
#else
#define dbgprt(_fmt...) do { } while (0)
#endif
//structure definition of linked list node
#ifndef WORDLISTH
#define WORDLISTH
// word frequency control
typedef struct WordNode Word_t;
struct WordNode {
const char *word;
unsigned long count;
Word_t *next;
Word_t *prev;
};
// word list control
typedef struct {
Word_t *head;
Word_t *tail;
unsigned long count;
} List_t;
void printWordList(List_t *list);
#endif
// create a list
List_t *
newList(void)
{
List_t *list;
list = calloc(1,sizeof(*list));
return list;
}
// function to check if word is in linked list
Word_t *
findWord(List_t *list,const char *myword)
{
Word_t *curr;
for (curr = list->head; curr != NULL; curr = curr->next) {
if (strcmp(curr->word,myword) == 0)
break;
}
return curr;
}
//function to add word to linked list
Word_t *
addWord(List_t *list,const char *word)
{
Word_t *match;
do {
// try to find existing word
match = findWord(list,word);
// word already exists
if (match != NULL)
break;
// create new node
match = malloc(sizeof(*match));
match->word = strdup(word);
match->count = 0;
match->next = NULL;
// attach to head of list
if (list->head == NULL)
list->head = match;
// append to tail of list
else
list->tail->next = match;
// set new tail of list
list->tail = match;
// advance list count
list->count += 1;
} while (0);
// increment the word frequency count
match->count += 1;
return match;
}
//function to update the count of word
void
updateCount(List_t *list,const char *myword)
{
Word_t *match;
match = findWord(list,myword);
if (match != NULL)
match->count += 1;
}
//function to print word list
//takes head node as argument
void
printWordList(List_t *list)
{
Word_t *curr;
for (curr = list->head; curr != NULL; curr = curr->next) {
printf("%s", curr->word);
printf(" %ld\n", curr->count);
}
}
int
main(int argc, char **argv)
{
char *filename = argv[1];
FILE *fp = fopen(filename, "r");
printf("FILE: %s\n", filename);
if (fp == NULL) {
printf("Error: unable to open the file ");
printf("%s", filename);
return 1;
}
// These are our word delimiters.
char *delim = " \t\n,:;'\".?!#$-><(){}[]|\\/*&^%#!~+=_";
char line[1000];
char *token;
// create list/head pointer
List_t *list = newList();
// iterate through each line in file
while (fgets(line, sizeof(line), fp) != NULL) {
// seperate each word
// first word of line
char *bp = line;
while (1) {
token = strtok(bp, delim);
bp = NULL;
if (token == NULL)
break;
dbgprt("TOKEN1: %s\n", token);
addWord(list,token);
}
}
printWordList(list);
return 0;
}
#Craig Estey has provided a great answer for you, so don't change your answer selection, but rather than just leave you a link in the comments, there are a couple of important ways of looking at list operations that may help, and you must use a memory/error checking program to validate your use of allocated memory, especially when dealing with list operations.
Take a node holding a string with a reference count of the additional number of times the string occurs, as in your case, e.g.
typedef struct node_t { /* list node */
char *s;
size_t refcnt;
struct node_t *next;
} node_t;
Iterating With Address of Node & Pointer to Node Eliminates Special Cases
Using both the address of the node and pointer to node is discussed in Linus on Understanding Pointers.
For example, where you need to check if (list->head == NULL) to add the first node to the list, if iterating with both the address of the node and pointer to node, you simply assign the allocated pointer to your new node to the address of the current node. This works regardless whether it is the first, middle or last node. It also eliminates having to worry about what the previous node was when removing nodes from the list. At the node to delete, you simply assign the contents of the next node to the current address and free the node that was originally there. This reduces your add node function to something similar to:
/** add node in sort order to list.
* returns pointer to new node on success, NULL otherwise.
*/
node_t *add_node (node_t **head, const char *s)
{
node_t **ppn = head, /* address of current node */
*pn = *head; /* pointer to current node */
while (pn) { /* iterate to last node */
if (strcmp (pn->s, s) == 0) { /* compare node string with string */
pn->refcnt += 1; /* increment ref count */
return *ppn;
}
ppn = &pn->next; /* ppn to address of next */
pn = pn->next; /* advance pointer to next */
}
return *ppn = create_node (s); /* allocate and return node */
}
(note: by delaying allocation for the new node and string (create_node (s) above), you avoid allocating until you know the string needs to be added -- simplifying memory handling)
As mentioned in the comments above, this combines your doesWordExist() traversal of the list and your addWord() traversal to find the end into a single traversal. If there are hundreds of thousands of nodes in your list, you don't want to traverse the list multiple times.
Using strdup() is Fine, but know it's POSIX not standard C
strdup() is handy for duplicating strings and assigning the result to a new pointer, but strdup() is provided by POSIX, so not all implementation will provide it. Additionally, strdup() allocates memory, so just as with any function that allocates memory, you must check that the result is not NULL before using the pointer that is returned. You can avoid the potential portability issue by writing a short equivalent. For example in the create_node() shown above, it does:
/** helper function to allocate node, and storage for string.
* copies string to node and initializes node->next pointer NULL
* and node->refcnt zero. returns pointer to allocated node on success,
* NULL otherwise.
*/
node_t *create_node (const char *s)
{
size_t len = strlen (s); /* get string length */
node_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate EVERY allocation */
perror ("create_node: malloc node");
return NULL;
}
if (!(node->s = malloc (len + 1))) { /* allocate for string */
perror ("create_node: malloc node->s");
free (node); /* on failure, free node before returning NULL */
return NULL;
}
memcpy (node->s, s, len+1); /* copy string to node */
node->refcnt = 0; /* initialize ref count */
node->next = NULL; /* initialze next NULL */
return node; /* return allocated & initialized node */
}
Freeing Allocated Memory
Any time you write code creating data structures, they need to be able to clean up after themselves in the event you want to remove a single node, or are done using the list. This becomes imperative when you create lists, etc. that are declared and used solely within functions below main() as the memory isn't freed on the function return. With main(), on return the program exits and will free all allocated memory. However if the list is created and used solely below main() a memory leak will result each time that function is called if the list memory is not freed before return. A function that frees all memory is short and easy to write, e.g.
/** delete all nodes in list */
void del_list (node_t *head)
{
node_t *pn = head; /* pointer to iterate */
while (pn) { /* iterate over each node */
node_t *victim = pn; /* set victim to current */
pn = pn->next; /* advance pointer to next */
free (victim->s); /* free current string */
free (victim); /* free current node */
}
}
(**no need to worry about the refcnt since you are deleting the list)
A short example including all of these points, as well as a function del_node() to remove a single node from the list (or reduce the refcnt without removing the node if the refcnt is non-zero) can be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
typedef struct node_t { /* list node */
char *s;
size_t refcnt;
struct node_t *next;
} node_t;
/** helper function to allocate node, and storage for string.
* copies string to node and initializes node->next pointer NULL
* and node->refcnt zero. returns pointer to allocated node on success,
* NULL otherwise.
*/
node_t *create_node (const char *s)
{
size_t len = strlen (s); /* get string length */
node_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate EVERY allocation */
perror ("create_node: malloc node");
return NULL;
}
if (!(node->s = malloc (len + 1))) { /* allocate for string */
perror ("create_node: malloc node->s");
free (node); /* on failure, free node before returning NULL */
return NULL;
}
memcpy (node->s, s, len+1); /* copy string to node */
node->refcnt = 0; /* initialize ref count */
node->next = NULL; /* initialze next NULL */
return node; /* return allocated & initialized node */
}
/** add node in sort order to list.
* returns pointer to new node on success, NULL otherwise.
*/
node_t *add_node (node_t **head, const char *s)
{
node_t **ppn = head, /* address of current node */
*pn = *head; /* pointer to current node */
while (pn) { /* iterate to last node */
if (strcmp (pn->s, s) == 0) { /* compare node string with string */
pn->refcnt += 1; /* increment ref count */
return *ppn;
}
ppn = &pn->next; /* ppn to address of next */
pn = pn->next; /* advance pointer to next */
}
return *ppn = create_node (s); /* allocate and return node */
}
/** print all nodes in list */
void prn_list (node_t *head)
{
if (!head) { /* check if list is empty */
puts ("list-empty");
return;
}
for (node_t *pn = head; pn; pn = pn->next) /* iterate over each node */
printf ("%-24s %4zu\n", pn->s, pn->refcnt); /* print string an refcount */
}
/** delete node with string s from list (for loop) */
void del_node (node_t **head, const char *s)
{
node_t **ppn = head; /* address of node */
node_t *pn = *head; /* pointer to node */
for (; pn; ppn = &pn->next, pn = pn->next) {
if (strcmp (pn->s, s) == 0) { /* does string match */
if (pn->refcnt) { /* ref count > 0 */
pn->refcnt -= 1; /* decrement ref count */
return; /* done */
}
*ppn = pn->next; /* set content at address to next */
free (pn); /* free original pointer */
break;
}
}
}
/** delete all nodes in list */
void del_list (node_t *head)
{
node_t *pn = head; /* pointer to iterate */
while (pn) { /* iterate over each node */
node_t *victim = pn; /* set victim to current */
pn = pn->next; /* advance pointer to next */
free (victim->s); /* free current string */
free (victim); /* free current node */
}
}
int main (int argc, char **argv) {
char buf[MAXC]; /* read buffer */
const char *delim = " \t\n.,;?!()"; /* strtok delimiters */
node_t *list = NULL; /* pointer to list (must initialize NULL */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) /* read all lines in file */
/* tokenize line based on delim */
for (char *p = strtok (buf, delim); p; p = strtok (NULL, delim)) {
if (ispunct(*p)) /* if word is punctionation, skip */
continue;
add_node (&list, p); /* add node or increment refcnt */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
puts ("string refcnt\n" /* heading */
"-------------------------------");
prn_list (list); /* print contents of list */
del_list (list); /* free all list memory */
return 0;
}
Take a look at the characters included in delim to use with strtok() as well as the use of ispunct() to skip tokens that end up beginning with punctuation (which allows hyphenated words, but skips hyphens used alone as sentence continuations, etc....)
Example Input File
$ cat dat/tr_dec_3_1907.txt
No man is above the law and no man is below it;
nor do we ask any man's permission when we require him to obey it.
Obedience to the law is demanded as a right; not asked as a favor.
(Theodore Roosevelt - December 3, 1907)
Example Use/Output
$ ./bin/lls_string_nosort_refcnt dat/tr_dec_3_1907.txt
string refcnt
-------------------------------
No 0
man 1
is 2
above 0
the 1
law 1
and 0
no 0
below 0
it 1
nor 0
do 0
we 1
ask 0
any 0
man's 0
permission 0
when 0
require 0
him 0
to 1
obey 0
Obedience 0
demanded 0
as 1
a 1
right 0
not 0
asked 0
favor 0
Theodore 0
Roosevelt 0
December 0
3 0
1907 0
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/lls_string_nosort_refcnt dat/tr_dec_3_1907.txt
==8528== Memcheck, a memory error detector
==8528== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8528== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8528== Command: ./bin/lls_string_nosort_refcnt dat/tr_dec_3_1907.txt
==8528==
string refcnt
-------------------------------
No 0
man 1
is 2
above 0
the 1
law 1
and 0
no 0
below 0
it 1
nor 0
do 0
we 1
ask 0
any 0
man's 0
permission 0
when 0
require 0
him 0
to 1
obey 0
Obedience 0
demanded 0
as 1
a 1
right 0
not 0
asked 0
favor 0
Theodore 0
Roosevelt 0
December 0
3 0
1907 0
==8528==
==8528== HEAP SUMMARY:
==8528== in use at exit: 0 bytes in 0 blocks
==8528== total heap usage: 73 allocs, 73 frees, 6,693 bytes allocated
==8528==
==8528== All heap blocks were freed -- no leaks are possible
==8528==
==8528== For counts of detected and suppressed errors, rerun with: -v
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
You already have a solution to your immediate problem, but going forward in your project consider some of the tips above to eliminate multiple traversals of your list, and the portability (and validation) issues surrounding strdup(). Good luck with your coding.
I am trying to load a hash table of node*(s)-
typedef struct node{
char word[LENGTH+1];
struct node* next;
}node;
(don't worry about length, it is defined in the file that calls this)
-into memory, but this:
// make hash table
node* hashtable[729];
node* new_node = malloc(sizeof(node));
node* cursor = NULL;
int bucket;
while(sscanf(dictionary,"%s",new_node->word) != 0)
{
bucket = hash(new_node->word);
cursor = hashtable[bucket];
while(cursor->next != NULL)
{
cursor = cursor->next;
}
cursor->next = new_node;
}
return true;
keeps turning up to be a segmentation fault (specifically the condition of the while loop). I am baffled, what is going on? Thank you in advance to any who helps! I really appreciate your help!
You need to allocate memory for each node that is going into your hash table. How's about something like the following:
/* make hash table */
node* hashtable[729];
/* initialise all buckets to NULL */
memset(hashtable, 0, sizeof(node*)*729);
node new_node; /* Use a stack node for the temporary */
new_node.next = NULL;
node** cursor = NULL;
int bucket;
while(sscanf(dictionary,"%s",new_node.word) != 0)
{
bucket = hash(new_node.word);
cursor = &hashtable[bucket];
while(*cursor != NULL)
{
cursor = &(*cursor)->next;
}
if ((*cursor = malloc(sizeof(node))) != NULL)
/* Copy from temporary to hashed node. Assumes structure is 'flat' */
**cursor = new_node;
else {
/* panic! */
}
}
return true;
Edit:
I've refactored some code and produced a standalone example that compiles and runs, For simplicity, I've employed a totally bogus hash function and reduced the number of buckets to fit its output of 0-25. I've tried to split out the hashtable 'object' and started the effort to be a little more disciplined to avoid buffer overruns, etc.
For the traversal of the linked list of nodes in a bucket of the hashtable, I've included two versions--one that uses the node** (pointer to a pointer) and another that doesn't--in an attempt to demonstrate the use of the double star. Change the #if 1 to #if 0 to use the "single star" version.
I hope that, collectively, these changes help clarify (more than they obscure) the original purpose, although I apologise for the verboseness of the code that follows:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define LENGTH 64
typedef struct node {
char word[LENGTH+1];
struct node * next;
} node;
typedef struct hashtable {
node * table[26];
} hashtable;
/* The cleverest 'hashing' function in the world ever! */
int hash(const char * str) {
if (str && str[0]) return tolower(str[0]) - 'a';
return 0;
}
/* Allocate a new node and initialise it with the given word */
node * node_create(const char * word) {
node * nd = NULL;
if (word && (nd = malloc(sizeof(node)))) {
strncpy(nd->word, word, sizeof(nd->word)-1);
nd->word[sizeof(nd->word) - 1] = '\0';
nd->next = NULL;
}
return nd;
}
/* Set all the buckets' pointers to NULL */
void hashtable_init(hashtable * ht) {
if (ht) memset(ht, 0, sizeof(hashtable));
}
/* Place the given node into the hashtable, taking responsibility for it */
void hashtable_insert_node(hashtable * ht, node * nd) {
if (ht && nd) {
#if 1 /* More succint version using node** */
/* Identify the bucket this node should go into */
node ** cursor = ht->table + hash(nd->word);
/* Append this node to this bucket's list of nodes */
while (*cursor != NULL) cursor = &(*cursor)->next;
*cursor = nd;
#else /* Version that avoids use of node** */
int bucket = hash(nd->word);
/* Identify the bucket this node should go into */
if (ht->table[bucket]) {
node * cursor = ht->table[bucket];
while (cursor->next) cursor = cursor->next;
cursor->next = nd;
} else {
ht->table[bucket] = nd;
}
#endif
nd->next = NULL; // Ensure the new node is the last in the list
}
}
/* Free the contents of the given hashtable */
void hashtable_free_contents(hashtable * ht) {
if (ht) {
int i;
for (i=0; i < sizeof(ht->table)/sizeof(ht->table[0]); ++i) {
node * cursor = ht->table[i];
while (cursor != NULL) {
node * next = cursor->next;
free(cursor);
cursor = next;
}
}
}
}
/* Dump the contents of the given hashtable to stdout */
void hashtable_dump(const hashtable * ht) {
if (ht) {
int i;
for (i=0; i < sizeof(ht->table)/sizeof(ht->table[0]); ++i) {
printf("Bucket %d:", i);
node * cursor = ht->table[i];
while (cursor != NULL) {
printf(" %s", cursor->word);
cursor = cursor->next;
}
printf("\n");
}
}
}
int main(int argc, char * argv[]) {
char dictionary[] = {
"apples "
"apricots "
"oranges "
"lemons "
"bananas "
"raspberries "
"carrots "
"tomatoes "
"aubergines "
"limes "
"blueberries "
"plums "
"pears "
"peaches "
"pineapples "
"tangerines "
"kiwis "
"passion_fruit "
"strawberries "
};
hashtable ht;
hashtable_init(&ht);
char * p = dictionary; /* Pointer for traversing the dictionary */
node new_node; /* Temporary node for storing word read from dictionary */
new_node.next = NULL;
int n; /* Number of bytes read from dictionary in sscanf call */
char format[16];
/* If a huge word is in the dictionary, guard against buffer overflow */
snprintf(format, sizeof(format), "%%%ds%%n", sizeof(new_node.word));
while(sscanf(p, format, new_node.word, &n) == 1) {
/* Insert (a copy of the) new node into hashtable */
hashtable_insert_node(&ht, node_create(new_node.word));
/* Move forwards through the dictionary */
p += n;
}
hashtable_dump(&ht);
hashtable_free_contents(&ht);
return 0;
}
Just allocate memory for each node of the hashtable and then dereference them.
i.e.
int i ;
for(i = 0; i < 729; ++i) {
hashtable[i] = malloc(sizeof(node));
hashtable[i]->next = NULL ;
}
I have a problem with parsing a .csv file. I have a struct world defined like this:
typedef struct world
{
char worldName[30];
int worldId;
char *message;
char **constellationArray;
struct world *next;
} tWorld;
And I have a .csv file designed like this (so the 'c' is for 'semi-Colon'):
worldId;worldName;message;constellationArray
1;K'tau;Planeta pod ochranou Freyra;Aquarius;Crater;Orion;Sagittarius;Cetus;Gemini;Earth
2;Martin's homeworld;Znicena;Aries;Sagittarius;Monoceros;Serpens;Caput;Scutum;Hydra;Earth
3;...
The task seems simple: write a method loadWorlds(char *file). Load the file and parse it. The number of constellations is not guaranteed. Each new line signals a new world and I have to create a linked list of these worlds. I have a rough idea of doing this, but I can't make it work. I have a method called tWorld *createWorld() which is implemented as such:
tWorld *createWorld() {
tWorld *world;
world = (*tWorld)malloc((sizeof(tWorld)));
return world;
}
I have to use this method inside my loadWorlds(char *file). Plus I have to serialize them into the linked list with this:
if (*lastWorld == NULL){
*lastWorld = nextWorld;
}else{
(*actualWorld)->next = nextWorld;
}
*actualWorld = nextWorld;
But I don't know when to use it. This is my rough sketch of loadWorlds(char *file):
void loadWorlds(char *file)
{
FILE *f;
char text[30];
char letter;
tWorld *lastWorld = NULL, *actualWorld = NULL, *world;
//f = fopen(file, "r");
if(!(f = fopen(file, "r")))
{
printf("File does not exist! \n");
while(!kbhit());
}
else
{
while(!(feof(f)) && (letter = fgetc(f))!= '\n')
{
if((znak = fgetc(f)) != ';')
{
}
}
}
}
I would be grateful for any ideas to make this work.
The question "How do I parse this file?... (Plus I have to serialize them into the linked list)" is a non-trivial undertaking when considered in total. Your "How do I parse this file?" is a question in its own right. The second part, regarding the linked list, is a whole separate issue that is not at all explained sufficiently, though it appears you are referring to a singularly-linked-list. There are as many different ways to approach this as there are labels of wine. I'll attempt to provide an example of one approah to help you along.
In the example below, rather than creating a single static character array worldName within a tWorld struct where all other strings are dynamically allocated, I've changed worldName to a character pointer as well. If you must use a static array of chars, that can be changed easily, but as long as you are allocating the remainder of the strings, it makes sense to allocate for worldName as well.
As to the parsing part of the question, you can use any number of library functions identified in the comments, or you can simply use a couple of pointers and step through each line parsing each string as required. Either approach is fine. The only benefit to using simple pointers, (aside from the learning aspect), is avoidance of repetative function calls which in some cases can be a bit more efficient. One note when parsing data from a line that has been dynamically allocated is to make sure you preserve the starting address for the buffer to insure the allocated memory can be properly tracked and freed. Some of the library functions clobber the original buffer (i.e. strtok, etc.) which can cause interesting errors if you pass the buffer itself without, in some way, preserving the original start address.
The function read_list_csv below parses each line read from the csv file (actually semi-colon separated values) into each of the members of the tWorld struct using a pair of character pointers to parse the input line. read_list_csv then calls ins_node_end to insert each of filled & allocated tWorld nodes into a singularly-linked circular linked-list. The parsing is commented to help explain the logic, but in summary it simply sets a starting pointer p to the beginning, then using an ending pointer ep checks each character in the line until a semi-colon ; is found, temporarily sets the ; to \0 (nul) and reads the string pointed to by p. The temporary \n is replaced with the original ; and the process repeats beginning with the following character, until the line has been completely parsed.
The linked-list part of your question is somewhat more involved. It is complicated by many linked-list examples being only partially explained and usually equivalently correct. Further, a linked-list is of little use unless you can add to it, read from it, remove from it, and get rid of it without leaking memory like a sieve. When you look at examples, note there are two primary forms linked-lists take. Either HEAD/TAIL lists or circular lists. Both can be either singularly or doubly linked. HEAD/TAIL lists generally use separate pointers for the list start or HEAD and the list end or TAIL node (generally set to NULL). circular lists simply have the end node next pointer point back to the beginning of the list. Both have their uses. The primary benefit to the circular list is that you can traverse the list from any node to any other node, regardless where you start in the list. (since there is no end-node, you can iterate through all nodes starting from any node).
The example below is a singularly linked circular list. It provides functions for creating nodes, inserting them into the list, counting the nodes, printing the entire list, removing nodes from the list, and deleting the list. Importantly, it frees all memory allocated to the list.
Go through both the parsing part of the example and the linked-list part of the example and let me know if you have questions. While the list implementation should be fairly solid, there may be some undiscovered issues. The datafile used for testing as well as the sample output is shown following the code. The code expects the datafile as the first argument and an optional (zero based) node to delete as a second argument (default: node 2):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 256
// #define wName 30
typedef struct world
{
// char worldName[wName];
char *worldName;
int worldId;
char *message;
char **constellationArray;
struct world *next;
} tWorld;
/* allocate & populate node */
tWorld *create_node (int wid, char *wnm, char *msg, char **ca);
/* insert node into list */
tWorld *ins_node_end (tWorld **list, int wid, char *wnm, char *msg, char **ca);
/* read data from file fname and add to list */
tWorld *read_list_csv (tWorld **list, char *fname);
/* return the number of nodes in list */
size_t getszlist (tWorld *list);
/* print all nodes in list */
void print_list (tWorld *list);
/* free memory allocated to tWorld list node */
void free_node (tWorld *node);
/* (zero-based) delete of nth node */
void delete_node (tWorld **list, int nth);
/* delete tWorld list & free allocated memory */
void delete_list (tWorld *list);
int main (int argc, char **argv)
{
if (argc < 2) {
fprintf (stderr, "error: insufficient input. Usage: %s <filename> [del_row]\n", argv[0]);
return 1;
}
char *fname = argv[1];
tWorld *myworld = NULL; /* create pointer to struct world */
read_list_csv (&myworld, fname); /* read fname and fill linked list */
printf ("\n Read '%zd' records from file: %s\n\n", getszlist (myworld), fname);
print_list (myworld); /* simple routine to print list */
int nth = (argc > 2) ? atoi (argv[2]) : 2;
printf ("\n Deleting node: %d\n\n", nth);
delete_node (&myworld, nth); /* delete a node from the list */
print_list (myworld); /* simple routine to print list */
delete_list (myworld); /* free memory allocated to list */
return 0;
}
/* allocate & populate node */
tWorld *create_node (int wid, char *wnm, char *msg, char **ca)
{
tWorld *node = NULL;
node = malloc (sizeof *node);
if (!node) return NULL;
node-> worldId = wid;
node-> worldName = wnm;
node-> message = msg;
node-> constellationArray = ca;
return node;
}
/* insert node into list */
tWorld *ins_node_end (tWorld **list, int wid, char *wnm, char *msg, char **ca)
{
tWorld *node = NULL;
if (!(node = create_node (wid, wnm, msg, ca))) return NULL;
if (!*list) { /* if empty, create first node */
node-> next = node;
*list = node;
} else { /* insert as new end node */
if (*list == (*list)-> next) { /* second node, no need to iterate */
(*list)-> next = node;
}
else /* iterate to end node & insert */
{
tWorld *iter = *list; /* second copy to iterate list */
for (; iter->next != *list; iter = iter->next) ;
iter-> next = node; /* insert node at end of list */
}
node-> next = *list; /* set next pointer to list start */
}
return *list; /* provides return as confirmation */
}
/* read list from file fname and add to list */
tWorld *read_list_csv (tWorld **list, char *fname)
{
FILE *fp = fopen (fname, "r");
if (!fp) {
fprintf (stderr, "%s() error: file open failed for '%s'\n", __func__, fname);
return NULL;
}
/* allocate and initialize all variables */
char *line = calloc (MAXL, sizeof *line);
char *p = NULL;
char *ep = NULL;
char *wnm = NULL;
int wid = 0;
int lcnt = 0;
char *msg = NULL;
char **ca = NULL;
size_t idx = 0;
while (fgets (line, MAXL, fp)) /* for each line in file */
{
if (lcnt++ == 0) continue; /* skip header row */
p = line;
idx = 0;
ep = p;
size_t len = strlen (line); /* get line length */
if (line[len-1] == '\n') /* strip newline from end */
line[--len] = 0;
while (*ep != ';') ep++; /* parse worldId */
*ep = 0;
wid = atoi (p);
*ep++ = ';';
p = ep;
while (*ep != ';') ep++; /* parse worldName */
*ep = 0;
wnm = strdup (p);
*ep++ = ';';
p = ep;
while (*ep != ';') ep++; /* parse message */
*ep = 0;
msg = strdup (p);
*ep++ = ';';
p = ep;
ca = calloc (MAXL, sizeof *ca); /* allocate constellationArray */
if (!ca) {
fprintf (stderr, "%s() error allocation failed for 'ca'.\n", __func__);
return NULL;
}
while (*ep) /* parse ca array elements */
{
if (*ep == ';')
{
*ep = 0;
ca[idx++] = strdup (p);
*ep = ';';
p = ep + 1;
/* if (idx == MAXL) reallocate ca */
}
ep++;
}
if (*p) ca[idx++] = strdup (p); /* add last element in line */
ins_node_end (list, wid, wnm, msg, ca); /* add to list */
}
/* close file & free line */
if (fp) fclose (fp);
if (line) free (line);
return *list;
}
/* return the number of nodes in list */
size_t getszlist (tWorld *list) {
const tWorld *iter = list; /* pointer to iterate list */
register int cnt = 0;
if (iter == NULL) {
fprintf (stdout,"%s(), The list is empty\n",__func__);
return 0;
}
for (; iter; iter = (iter->next != list ? iter->next : NULL)) {
cnt++;
}
return cnt;
}
/* print all nodes in list */
void print_list (tWorld *list) {
const tWorld *iter = list; /* pointer to iterate list */
register int idx = 0;
char *stub = " ";
if (iter == NULL) {
fprintf (stdout,"%s(), The list is empty\n",__func__);
return;
}
for (; iter; iter = (iter->next != list ? iter->next : NULL)) {
printf (" %2d %-20s %-20s\n",
iter-> worldId, iter-> worldName, iter-> message);
idx = 0;
while ((iter-> constellationArray)[idx])
printf ("%38s %s\n", stub, (iter-> constellationArray)[idx++]);
}
}
/* free memory allocated to tWorld list node */
void free_node (tWorld *node)
{
if (!node) return;
register int i = 0;
if (node-> worldName) free (node-> worldName);
if (node-> message) free (node-> message);
while (node-> constellationArray[i])
free (node-> constellationArray[i++]);
if (node-> constellationArray)
free (node-> constellationArray);
free (node);
}
/* (zero-based) delete of nth node */
void delete_node (tWorld **list, int nth)
{
/* test that list exists */
if (!*list) {
fprintf (stdout,"%s(), The list is empty\n",__func__);
return;
}
/* get list size */
int szlist = getszlist (*list);
/* validate node to delete */
if (nth >= szlist || nth < 0) {
fprintf (stderr, "%s(), error: delete out of range (%d). allowed: (0 <= nth <= %d)\n",
__func__, nth, szlist-1);
return;
}
/* create node pointers */
tWorld *victim = *list;
tWorld *prior = victim;
/* if nth 0, prior is last, otherwise node before victim */
if (nth == 0) {
for (; prior->next != *list; prior = prior->next) ;
} else {
while (nth-- && victim-> next != *list) {
prior = victim;
victim = victim-> next;
}
}
/* non-self-reference node, rewire next */
if (victim != victim->next) {
prior-> next = victim-> next;
/* if deleting node 0, change list pointer address */
if (victim == *list)
*list = victim->next;
} else { /* if self-referenced, last node, delete list */
*list = NULL;
}
free_node (victim); /* free memory associated with node */
}
/* delete tWorld list */
void delete_list (tWorld *list)
{
if (!list) return;
tWorld *iter = list; /* pointer to iterate list */
for (; iter; iter = (iter->next != list ? iter->next : NULL))
if (iter) free_node (iter);
}
input test data file:
$ cat dat/struct.csv
worldId;worldName;message;constellationArray
1;K'tau;Planeta pod ochranou Freyra;Aquarius;Crater;Orion;Sagittarius;Cetus;Gemini;Earth
2;Martin's homeworld;Znicena;Aries;Sagittarius;Monoceros;Serpens;Caput;Scutum;Hydra;Earth
3;Martin's homeworld2;Znicena2;Aries2;Sagittarius2;Monoceros2;Serpens2;Caput2;Scutum2;Hydra2;Earth2
4;Martin's homeworld3;Znicena3;Aries3;Sagittarius3;Monoceros3;Serpens3;Caput3;Scutum3;Hydra3;Earth3
output:
$ ./bin/struct_ll_csv dat/struct.csv 1
Read '4' records from file: dat/struct.csv
1 K'tau Planeta pod ochranou Freyra
Aquarius
Crater
Orion
Sagittarius
Cetus
Gemini
Earth
2 Martin's homeworld Znicena
Aries
Sagittarius
Monoceros
Serpens
Caput
Scutum
Hydra
Earth
3 Martin's homeworld2 Znicena2
Aries2
Sagittarius2
Monoceros2
Serpens2
Caput2
Scutum2
Hydra2
Earth2
4 Martin's homeworld3 Znicena3
Aries3
Sagittarius3
Monoceros3
Serpens3
Caput3
Scutum3
Hydra3
Earth3
Deleting node: 1
1 K'tau Planeta pod ochranou Freyra
Aquarius
Crater
Orion
Sagittarius
Cetus
Gemini
Earth
3 Martin's homeworld2 Znicena2
Aries2
Sagittarius2
Monoceros2
Serpens2
Caput2
Scutum2
Hydra2
Earth2
4 Martin's homeworld3 Znicena3
Aries3
Sagittarius3
Monoceros3
Serpens3
Caput3
Scutum3
Hydra3
Earth3
Visual Studio 2008 - compiling in C
I am writing a linked list application but when I try and free each node I get an exception thrown. The only thing I can think of is that I allocated my memory in the add function, and maybe globaly I cannot free it in another function. Appart from that I can't think of anything.
Many thanks for any advice,
#include <stdio.h>
#include <stdlib.h>
static struct convert_temp
{
size_t cel;
size_t fah;
struct convert_temp *next;
} *head = NULL, *tail = NULL;
=======
/** Add the new converted temperatures on the list */
void add(size_t cel, size_t fah)
{
struct convert_temp *node_temp = NULL; /* contain temp data */
node_temp = malloc(sizeof(node_temp));
if(node_temp == NULL)
{
fprintf(stderr, "Cannot allocate memory [ %s ] : [ %d ]\n",
__FUNCTION__, __LINE__);
exit(0);
}
/* Assign data */
node_temp->cel = cel;
node_temp->fah = fah;
node_temp->next = NULL;
if(head == NULL)
{
/* The list is at the beginning */
head = node_temp; /* Head is the first node = same node */
tail = node_temp; /* Tail is also the last node = same node */
}
else
{
/* Append to the tail */
tail->next = node_temp;
/* Point the tail at the end */
tail = node_temp;
}
}
=====
/** Free all the memory that was used to allocate the list */
void destroy()
{
/* Create temp node */
struct convert_temp *current_node = head;
/* loop until null is reached */
while(current_node)
{
struct convert_temp *next = current_node->next;
/* free memory */
free(current_node);
current_node = next;
}
/* Set to null pointers */
head = NULL;
tail = NULL;
}
node_temp = malloc(sizeof(node_temp)); is allocating the size of a struct pointer instead of the struct, you should be using sizeof(*node_temp).
This line does not allocate the correct amount of memory:
node_temp = malloc(sizeof(node_temp));
It should instead be this:
node_temp = malloc(sizeof *node_temp);
Change:
node_temp = malloc(sizeof(node_temp));
to:
node_temp = malloc(sizeof(struct convert_temp));
and it will work. sizeof(node_temp) is the size of a pointer (most likely 4 or 8 bytes); you want to allocate the size of the structure