Function to add a node to linked list not working | c - c

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.

Related

Remove multiple strings based on the given substring

I'm trying to create a code where there are the names (e.g. Andy, Barry, Matilda) and the names will be removed when I enter a certain substring (e.g. substring is y, therefore Andy and Barry will be removed. The only one left is Matilda). Can anyone offer me assistance?
My code is:
void del(char key[])
{
if(head == NULL)
{
printf("There's no data\n");
}
else{
curr = head;
while(curr != NULL && strcmp(curr->name, key) != 0)
{
curr = curr->next;
}
if(curr == NULL)
{
printf("Node is not in the list\n");
}
if(curr == head & curr == tail)
{
free(curr);
head = tail = NULL;
}
else if(curr == head)
{
head = head->next;
free(curr);
head->prev = NULL;
}
else if(curr == tail)
{
tail = tail->prev;
free(curr);
tail->next = NULL;
}
else
{
curr->prev->next = curr->next;
curr->next->prev = curr->prev;
free(curr);
}
}
}
Your code correctly removes a single list node, except when key is not found - in this case, a return after printf("Node is not in the list\n"); is missing.
To remove multiple nodes, we have to loop over the list, continuing also if a match is found and deleted. To print the message Node is not in the list only if nothing was found, we can use a flag which is tested after the loop. So, you can replace the content of your outer else block e. g. with
void *next;
int deleted = 0;
for (curr = head; curr; curr = next)
{ next = curr->next;
if (strstr(curr->name, key))
{ deleted = 1;
// the rest of this block is your code unchanged
if(curr == head & curr == tail)
{
free(curr);
head = tail = NULL;
}
else if(curr == head)
{
head = head->next;
free(curr);
head->prev = NULL;
}
else if(curr == tail)
{
tail = tail->prev;
free(curr);
tail->next = NULL;
}
else
{
curr->prev->next = curr->next;
curr->next->prev = curr->prev;
free(curr);
}
}
}
if (!deleted) printf("Node is not in the list\n");
You have not offered a lot of information to go on, but I take that is being new to the community. From what you do provide, it is clear that, at minimum, you have a Doubly-Linked-List where you have a string as the data member. It is also clear that you have declared both the head and tail pointer as global variables (not a great practice, as you should pass any information required by your function as a parameter, but for this learning exercise it provides minimal simplification)
From what you describe you want your del function to do, you want to iterate over your linked-list testing whether the name member contains the substring key and if so, you want to delete that node from your list.
You have two primary problems:
you use the wrong string function to check for a substring in your name member. strcmp() will only find the nodes where the complete name matches your key. Instead you want strstr() where you can match key anywhere within name;
you over-complicate the node deletion by attempting to use a pointer to the current node alone to iterate over the list, instead of using both a pointer and the address for the current node. See Linus on Understanding Pointers
To correct your fist problem, you simply need to change strcmp() to strstr() fot match key anywhere in name, e.g.
...
if (strstr(curr->name, key) != NULL) { /* strstr, not strcmp, to find key */
...
To solve the second problem, you need to rework your del function. To do so, you have to understand how iterating while removing multiple-nodes differs from iterating to find a single-node to delete. When looping to find a single-node to delete, you simply advance to the next node in your list on each iteration until the node is found and then you delete that node.
You cannot do that when potentially deleting multiple nodes from your list. Why? When you delete a node with a matching substring, the next node in the list takes its place. You can't just advance to the next node after this deletion because the node that is now the current node after you delete the first may also contain the substring you want to find. If you just blindly advance to the next node and the current after deletion also contains the substring, you will skip over deleting that node.
What this means from a code standpoint is you need to add an else clause below your if, e.g.
if (strstr(curr->name, key) != NULL) { /* strstr, not strcmp, to find key */
...
else
...
Under your if clause, the next node replaces the current after deletion, so you do NOT advance again to the next node in your list. That way the new current node will be checked for the matching substring on the next iteration. You only advance to the next node under your else clause if key does not match.
When iterating with both a pointer to the current node along with the address of the current node, you do not have to handle special cases. You will always set the content at the current address to the next node in your list. (head doesn't change, because you have set the struct at that address to the next on deletion) The only check you need is to check if you are deleting the tail node. In that case you need to update the tail node to point to the previous node as you will be deleting what tail currently points to. Otherwise, all you need to do is update the ->prev pointer of the node you moved into the current address to point to the previous node in the list.
With that in mind, your del function reduces to:
void del (char key[])
{
if (head == NULL) { /* check empty */
puts ("list-empty");
return;
}
node_t **ppnode = &head, /* address of current node */
*curr = head; /* pointer to current node */
while (curr) {
if (strstr(curr->name, key) != NULL) { /* strstr, not strcmp, to find key */
*ppnode = curr->next; /* fill address w/next node */
if (curr != tail) /* if not tail */
(*ppnode)->prev = curr->prev; /* set ->prev to prev node */
else /* otherwise */
tail = curr->prev; /* update tail pointer */
free (curr); /* free node */
curr = *ppnode; /* set new current node */
}
else { /* node to keep */
ppnode = &curr->next; /* set address to addres of next */
curr = curr->next; /* advance to next node */
}
}
}
Without knowing more about your code, all we can do is write a short example that adds your strings as nodes in a list (using a fixed name to simplify things). A short example like:
int main (void) {
add ("Andy"); /* add nodes to list */
add ("Barry");
add ("Matilda");
prnfwd(); /* print forward and reverse */
prnrev();
putchar ('\n');
del ("y"); /* delete nodes containing substring "y" */
prnfwd(); /* print forward and reverse */
prnrev();
del_list(); /* free allocated memory */
}
That adds the nodes, iterates over the list in both directions, calls del ("y"); to delete all nodes where the string contains the substring "y" (note that is different from the character 'y'), and then iterates over the list again in both direction outputting what remains before freeing all memory in the list.
Example Use/Output
The result given your example strings would be:
$ ./bin/lldglobaldelkey
Andy Barry Matilda
Matilda Barry Andy
Matilda
Matilda
The full implementation of the example is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXNM 64
typedef struct node_t {
char name[MAXNM];
struct node_t *prev, *next;
} node_t;
node_t *head, *tail;
/** add node at end of list, update tail to end */
node_t *add (const char *s)
{
node_t *node = malloc (sizeof *node); /* allocate node */
if (!node) /* validate allocation */
return NULL;
strcpy (node->name, s); /* initialize new node */
node->prev = node->next = NULL;
if (!head) /* if 1st node, node is head/tail */
head = tail = node;
else { /* otherwise */
node->prev = tail; /* set prev to tail */
tail->next = node; /* add at end, update tail pointer */
tail = node;
}
return node; /* return new node */
}
/* print list forward */
void prnfwd (void)
{
if (!head) { /* check empty */
puts ("list-empty");
return;
}
for (node_t *n = head; n; n = n->next) /* iterate over nodes - forward */
printf (" %s", n->name);
putchar ('\n');
}
/* print list reverse */
void prnrev (void)
{
if (!head) { /* check empty */
puts ("list-empty");
return;
}
for (node_t *n = tail; n; n = n->prev) /* iterate over nodes - reverse */
printf (" %s", n->name);
putchar ('\n');
}
/** delete all nodes in list */
void del_list (void)
{
node_t *n = head;
if (!head) { /* check empty */
puts ("list-empty");
return;
}
while (n) { /* iterate over nodes - forward */
node_t *victim = n; /* save ptr to node to delete */
n = n->next; /* advance to next */
free (victim); /* delete node */
}
head = tail = NULL; /* set pointers NULL */
}
void del (char key[])
{
if (head == NULL) { /* check empty */
puts ("list-empty");
return;
}
node_t **ppnode = &head, /* address of current node */
*curr = head; /* pointer to current node */
while (curr) {
if (strstr(curr->name, key) != NULL) { /* strstr, not strcmp, to find key */
*ppnode = curr->next; /* fill address w/next node */
if (curr != tail) /* if not tail */
(*ppnode)->prev = curr->prev; /* set ->prev to prev node */
else /* otherwise */
tail = curr->prev; /* update tail pointer */
free (curr); /* free node */
curr = *ppnode; /* set new current node */
}
else { /* node to keep */
ppnode = &curr->next; /* set address to addres of next */
curr = curr->next; /* advance to next node */
}
}
}
int main (void) {
add ("Andy"); /* add nodes to list */
add ("Barry");
add ("Matilda");
prnfwd(); /* print forward and reverse */
prnrev();
putchar ('\n');
del ("y"); /* delete nodes containing substring "y" */
prnfwd(); /* print forward and reverse */
prnrev();
del_list(); /* free allocated memory */
}
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/lldglobaldelkey
==10704== Memcheck, a memory error detector
==10704== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10704== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10704== Command: ./bin/lldglobaldelkey
==10704==
Andy Barry Matilda
Matilda Barry Andy
Matilda
Matilda
==10704==
==10704== HEAP SUMMARY:
==10704== in use at exit: 0 bytes in 0 blocks
==10704== total heap usage: 4 allocs, 4 frees, 1,264 bytes allocated
==10704==
==10704== All heap blocks were freed -- no leaks are possible
==10704==
==10704== For counts of detected and suppressed errors, rerun with: -v
==10704== 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.
I hope this is close to what your implementation is. Look things over and let me know if you have further questions.

Adding strings to a linked list result in seg fault

I am trying to add strings that I am reading from a text file to a linked list.
Since I don't know how long the file or the string is , I want to do this dynamically.
But somewhere along the line I get a segmentation fault.
I have tried everything but I think I overlooked something crucial.
Could someone tell me what I am doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node {
char *name;
int age;
struct node* next;
}node;
node *add(node *head, char* n_m){
node *new_node;
new_node = (node*)malloc(sizeof(node));
if(new_node == NULL)
printf("Fehler bei Speicher reservierung...");
new_node->name = (char*)malloc(100*sizeof(char));
if(new_node->name == NULL)
printf("Fehler bei Speicher reservierung...");
strcpy(new_node->name, n_m);
if(head == NULL){
head = new_node;
head->next = NULL;
return head;
}
node *current;
current = head;
while(current->next != NULL){
current = current->next;
}
current->next = new_node;
new_node->next = NULL;
return head;
}
void print(node *head){
node *current;
current = head;
while(current != NULL){
printf("%s\n", current->name);
current = current->next;
}
}
int main(){
node *head = NULL;
char character;
FILE *fp;
fp = fopen("test.txt", "r");
while ((character = fgetc(fp)) != EOF) {
char *n_m;
n_m = (char*)malloc(100 * sizeof(char));
if(n_m == NULL)
printf("Fehler bei Speicher reservierung...");
int i = 0;
while (character != ' ') {
n_m[i++] = character;
character = fgetc(fp);
}
n_m[++i] = '\0'; // NULL-terminate
head = add(head, n_m);
free(n_m);
}
print(head);
return 0;
}
Your biggest issue is your read of characters does not trap EOF and will continue reading after EOF is encountered, causing i to exceed your array bounds invoking Undefined Behavior leading to your SegFault. The code in question is:
while (character != ' ') {
n_m[i++] = character;
character = fgetc(fp);
}
Since POSIX files do not end with a ' ' (space), but instead a '\n', your read of the last word in the file does not stop at EOF. You further have problems with multiple spaces together repeatedly writing nodes containing the empty-string to your list.
You also fail to handle any other whitespace other than space, meaning you are including '\t', '\n', vertical tab, etc.. within the words you store. Instead of checking space with ' ', use the isspace() macro included in ctype.h.
n_m[++i] = '\0'; should be n_m[i] = '\0';. You increment i with n_m[i++] = character;. You do NOT want to increment i again with the pre-increment operator before nul-terminating your string. That results in the last character in the string being indeterminate (again invoking undefined behavior when a read of the string is attempted)
Fixing those issues (and using c instead of character, ndx instead of i and buf instead of n_m), your read and add() to your list would resemble:
while ((c = fgetc(fp)) != EOF) { /* read each char in file */
if (isspace(c) || ndx == MAXC - 1) { /* is space or buf full? */
if (in) { /* were we within word? */
buf[ndx] = 0; /* nul-terminate */
head = add (head, buf); /* add node to list */
}
if (ndx < MAXC - 1) /* buffer not full */
in = 0; /* set in flag zero */
ndx = 0; /* reset index zero */
}
else { /* otherwise */
buf[ndx++] = c; /* add char to buf */
in = 1; /* set in flag 1 */
}
}
(note: using the variable in as an in/out flag to keep track of whether you are within a word reading characters, or between words reading whitespace solves the problem you would have with multiple whitespace characters in sequences, e.g. "hello world")
Optional, but helpful, when allocating nodes that also contain members that need to be allocated is to write a createnode() function that fully Allocates and Initializes all node members rather than putting that code in add(). It keeps things clean and ensures every node you allocate is fully initialized before its use in add(). For example:
/** create new node, allocate and initialize all member values,
* return pointer to node on success, NULL otherwise.
*/
node *createnode (char *s)
{
node *new_node;
new_node = malloc (sizeof *new_node); /* allocate/validate node */
if (new_node == NULL) {
perror ("malloc-Fehler bei Speicher reservierung...");
return NULL;
}
new_node->name = malloc (strlen (s) + 1); /* allocate/validate name */
if (new_node->name == NULL) {
perror ("malloc-Fehler bei Speicher reservierung...");
return NULL;;
}
strcpy (new_node->name, s); /* initialize all node values */
new_node->age = 0;
new_node->next = NULL;
return new_node; /* return newly allocated/initialized node */
}
Then your add() function reduces to:
/** add node containing allocated string 'n_m' to list, return pointer
* to 1st node in list on success, exit with failure otherwise.
*/
node *add (node *head, char *n_m)
{
node *new_node = createnode (n_m); /* allocate/initialize new node */
node *current = head; /* pointer to current head */
if (new_node == NULL) /* validate allocation */
exit (EXIT_FAILURE);
if (!head) /* handle 1st node */
return new_node;
while (current->next != NULL) /* iterate to end of list */
current = current->next;
current->next = new_node; /* set next node to new_node */
return head; /* return pointer to head */
}
Putting it altogether and adding a del_list() function to free all allocated memory for the list, you could do the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024
typedef struct node {
char *name;
int age;
struct node *next;
} node;
/** create new node, allocate and initialize all member values,
* return pointer to node on success, NULL otherwise.
*/
node *createnode (char *s)
{
node *new_node;
new_node = malloc (sizeof *new_node); /* allocate/validate node */
if (new_node == NULL) {
perror ("malloc-Fehler bei Speicher reservierung...");
return NULL;
}
new_node->name = malloc (strlen (s) + 1); /* allocate/validate name */
if (new_node->name == NULL) {
perror ("malloc-Fehler bei Speicher reservierung...");
return NULL;;
}
strcpy (new_node->name, s); /* initialize all node values */
new_node->age = 0;
new_node->next = NULL;
return new_node; /* return newly allocated/initialized node */
}
/** add node containing allocated string 'n_m' to list, return pointer
* to 1st node in list on success, exit with failure otherwise.
*/
node *add (node *head, char *n_m)
{
node *new_node = createnode (n_m); /* allocate/initialize new node */
node *current = head; /* pointer to current head */
if (new_node == NULL) /* validate allocation */
exit (EXIT_FAILURE);
if (!head) /* handle 1st node */
return new_node;
while (current->next != NULL) /* iterate to end of list */
current = current->next;
current->next = new_node; /* set next node to new_node */
return head; /* return pointer to head */
}
void print (node * head)
{
node *current;
current = head;
while (current != NULL) {
printf ("%s\n", current->name);
current = current->next;
}
}
/** delete all nodes in list */
void del_list (node *head)
{
node *pn = head; /* pointer to iterate */
while (pn) { /* iterate over each node */
node *victim = pn; /* set victim to current */
pn = pn->next; /* advance pointer to next */
free (victim->name); /* free current string */
free (victim); /* free current node */
}
}
int main (int argc, char **argv) {
char buf[MAXC];
int c = 0, in = 0, ndx = 0;
node *head = 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 ((c = fgetc(fp)) != EOF) { /* read each char in file */
if (isspace(c) || ndx == MAXC - 1) { /* is space or buf full? */
if (in) { /* were we within word? */
buf[ndx] = 0; /* nul-terminate */
head = add (head, buf); /* add node to list */
}
if (ndx < MAXC - 1) /* buffer not full */
in = 0; /* set in flag zero */
ndx = 0; /* reset index zero */
}
else { /* otherwise */
buf[ndx++] = c; /* add char to buf */
in = 1; /* set in flag 1 */
}
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
print (head); /* print list */
del_list (head); /* free all allocated memory */
}
Example Input File
$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Example Use/Output
$ ./bin/ll_name_age dat/captnjack.txt
This
is
a
tale
Of
Captain
Jack
Sparrow
A
Pirate
So
Brave
On
the
Seven
Seas.
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/ll_name_age dat/captnjack.txt
==18265== Memcheck, a memory error detector
==18265== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18265== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18265== Command: ./bin/ll_name_age dat/captnjack.txt
==18265==
This
is
a
tale
Of
Captain
Jack
Sparrow
A
Pirate
So
Brave
On
the
Seven
Seas.
==18265==
==18265== HEAP SUMMARY:
==18265== in use at exit: 0 bytes in 0 blocks
==18265== total heap usage: 35 allocs, 35 frees, 6,132 bytes allocated
==18265==
==18265== All heap blocks were freed -- no leaks are possible
==18265==
==18265== For counts of detected and suppressed errors, rerun with: -v
==18265== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
You need to guard against reading beyond the end of file and against going beyond the memory you've allocated. It may also be possible that your file had a "long line" right at 100 characters by both postfixing and prefixing i you went passed your end of buffer.
while ((character = fgetc(fp)) != EOF) {
char *n_m;
n_m = (char*)malloc(100 * sizeof(char));
if(n_m == NULL)
printf("Fehler bei Speicher reservierung...");
int i = 0;
while ((character != ' ')
&& (character != EOF)
&& (i < 100)) {
n_m[i++] = character;
character = fgetc(fp);
}
// don't postfix add about then prefix add below
if (i >= 100) {
… there is a problem …
}
n_m[i] = '\0'; // NULL-terminate
head = add(head, n_m);
free(n_m);
}
You might consider something more like this
#define BUFF_SIZE 100
char buff[BUFF_SIZE];
buff[0] = '\0';
int i = 0;
while (((character = fgetc(fp)) != EOF)
&& (i < BUFF_SIZE)) {
buff[i++] = character;
character = fgetc(fp);
if (character = ' ') {
buff[i] = '\0'; // NULL-terminate
head = add(head, buff);
i = 0;
buff[0] = '\0';
}
}
if (i >= BUFF_SIZE) { … there is a problem … }
This does a couple of useful things. One is your buffer is statically allocated and it's size is controlled by a single #define. Second, it reduces the number of loops involved which can improve readability.

CSV file advanced parsing

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

Linked list questions

Hey so i am trying to create a linked list. The following code segment opens the file for reading, which then gets passed into a function to take the string break it down and place it into a node which is suppose to be placed into the list in an appropriate location.
void print_list(struct vm_node *root);
int addNodeBottom(char *val, struct vm_node *head);
struct stock_item* setupNode(char *line);
int main(int argc, char * argv[]) {
struct vm vm;
struct menu_item menu_items[NUM_MENU_ITEMS];
struct vm_node *vmNode;
vmNode = malloc(sizeof(struct vm_node));
/* The UNUSED() function is designed to prevent warnings while your
* code is only partially complete. Delete these 4 function calls once
* you are using the data structures declared above in your own code */
UNUSED(argc);
UNUSED(argv);
UNUSED(vm);
UNUSED(menu_items);
if (argc != 3) {
printf("insuffcient arguments \n");
return EXIT_SUCCESS;
}
/*open stock file*/
char* fileName = argv[1];
FILE *file;
file = fopen(fileName, "r+");
char buf[256];
vmNode->next = NULL;
while (fgets(buf, sizeof buf, file) != NULL) {
addNodeBottom(buf,vmNode);
}
print_list(vmNode);
/* Test reason for reaching NULL. */
if (feof(file)) /* if failure caused by end-of-file condition */
puts("End of file reached");
else if (ferror(file)) /* if failure caused by some other error */
{
perror("fgets()");
fprintf(stderr, "fgets() failed in file %s at line # %d\n", __FILE__,
__LINE__ - 9);
exit(EXIT_FAILURE);
}
fclose(file);
return EXIT_SUCCESS;
}
the following function is how i have described the setupNode function.
struct stock_item* setupNode(char *line) {
struct stock_item *root;
root = malloc(sizeof(struct stock_item));
char *ptr;
const char del[2] = "|";
const char delm[2] = ".";
char *prices;
strcpy(root->id, strtok_r(line, del, &ptr)); // returns the ID and stores in in the root node.
strcpy(root->name, strtok_r(NULL, del, &ptr)); // returns the description and stores it in the root node.
strcpy(root->description, strtok_r(NULL, del, &ptr)); // returns the description and stores it in the root node.
prices = strtok_r(NULL, del, &ptr); // returns a string of the price for vm_item.
int dol = atoi(strtok(prices, delm));
int cent = atoi(strtok(NULL, delm));
root->price.dollars = dol;
root->price.cents = cent;
int quantity = atoi(strtok_r(NULL, del, &ptr)); // returns how many items are in stock.
root->on_hand = quantity;
return root;
}
This is the addNode function
int addNodeBottom(char *val, struct vm_node *head){
//create new node
struct vm_node *newNode = malloc(sizeof(struct vm_node));
if(newNode == NULL){
printf("%s", "Unable to allocate memory for new node\n");
exit(-1);
}
newNode->data = setupNode(val);
newNode->next = NULL; // Change 1
//check for first insertion
if(head->next == NULL){
head->data = newNode->data;
head->next = newNode;
}
else
{
//else loop through the list and find the last
//node, insert next to it
struct vm_node *current = head;
while (TRUE) { // Change 2
if(current->next == NULL)
{
current->next = newNode;
break; // Change 3
}
current = current->next;
};
}
free(newNode);
return 0;
}
and the printList function
void print_list(struct vm_node *root) {
while (root) {
printf("%s ", root->data->id);
root = root->next;
}
printf("\n");
}
Here is the typeDefs
#ifndef VM_TYPE
#define VM_TYPE
#define IDLEN 5
#define NAMELEN 40
#define DESCLEN 255
#define NUMDENOMS 8
#define UNUSED(var) (void)var
#define COIN_COUNT 20
#define DEFAULT_ONHAND 20
/* Type definition for our boolean type */
typedef enum truefalse
{
FALSE, TRUE
} BOOLEAN;
/* Each price will have a dollars and a cents component */
struct price
{
unsigned dollars,cents;
};
/* The different denominations of coins available */
enum denomination
{
FIVE_CENTS, TEN_CENTS, TWENTY_CENTS, FIFTY_CENTS, ONE_DOLLAR,
TWO_DOLLARS, FIVE_DOLLARS, TEN_DOLLARS
};
/* Each coin in the coins array will have a denomination (20 cents,
* 50 cents, etc) and a count - how many of that coin do we have on hand
*/
struct coin
{
enum denomination denom;
unsigned count;
};
/* The data structure that holds the data for each item of stock
*/
struct stock_item
{
char id[IDLEN+1];
char name[NAMELEN+1];
char description[DESCLEN+1];
struct price price;
unsigned on_hand;
};
/* The data structure that holds a pointer to the stock_item data and a
* pointer to the next node in the list
*/
struct vm_node
{
struct stock_item * data;
struct vm_node * next;
};
/* The head of the list - has a pointer to the rest of the list and a
* stores the length of the list
*/
struct vm_list
{
struct vm_node * head;
unsigned length;
};
/* This is the head of our overall data structure. We have a pointer to
* the vending machine list as well as an array of coins.
*/
struct vm
{
struct vm_list * item_list;
struct coin coins[NUMDENOMS];
char * foodfile;
char * coinsfile;
};
#endif
and the format of the text file that is being read in for parsing.
I0001|Coke |375 ml Can of coke |3.50|50
I0002|Pepsi |375 ml Can of pepsi |3.00|20
I0003|Lemon Cheesecake|A delicious, 1/8 size slice of cheesecake |4.00|10
I0004|Mars Bar |A delicious 50 g Mars Bar chilled just the way you like it.|3.00|20
I0005|Lemon Tart |A delicious lemon butter tart with a pastry based |3.75|12
The output when trying to print the list is complete garbage so any thoughts?
You have undefined behavior, because in addNodeBottom you make e.g. current->next point the new node you allocate, then you free the new node, so the pointer in current->next now point to unallocated memory.
Also, when setting up the first node (when head->next is NULL) then don't set the next pointer of head, let it be NULL. Instead to distinguish between an empty list or not, check for a non-null data field:
if (head->data == NULL)
{
// List is empty
}
else
{
// List is not empty
}
Other tips: There's no need to allocate a new node until you actually add it to the list. And the loop to find the last node in the list can be simplified to this:
vm_node *current;
for (current = head; current->next != NULL; current = current->next)
{
// Empty loop body
}
After the above loop current will be the last node in the list, and you can now allocate a new node.
If I would rewrite the addNodeBottom function (without modifying the function signature), it would look something like this (without any error handling):
int addNodeBottom(char *val, struct vm_node *head){
//create new node
stock_item *data = setupNode(val);
if (head->data == NULL)
head->data = data;
else
{
vm_node *current;
for (current = head; current->next != NULL; current = current->next)
;
current->next = malloc(sizeof(*current->next));
current->next->data = data;
current->next->next = NULL;
}
return 0;
}
Note: You must set vmNode->data = NULL before calling the above function for the first time, not only vmNode->next.
The main issue is because you are creating a new Node, storing data in it and then deleting the new node using free.
I understand your logic as it is now in the list so you do not need it anymore. But that is not the issue. In the list you merely put a pointer that points to the new node you created. You do not copy the new node in the list. You only put a pointer that points to the allocated memory for the node you created. If you free that part of the memory then it no longer exists and that part can be overwritten by any other application or yours. So it ends up being garbage.

Creating a singly linked list in C

I'm trying to create a singly linked list from an input text file for an assignment. I'm trying to do it a little bit at a time so I know my code is not complete. I tried creating the head pointer and just printing out its value and I can't even get that to work, but I'm not sure why. I included the struct, my create list, and print list functions. I didn't include the open file since that part works.
typedef struct List
{
struct List *next; /* pointer to the next list node */
char *str; /* pointer to the string represented */
int count; /* # of occurrences of this string */
} LIST;
LIST *CreateList(FILE *fp)
{
char input[LINE_LEN];
LIST *root; /* contains root of list */
size_t strSize;
LIST *newList; /* used to allocate new list members */
while (fscanf(fp, BUFFMT"s", input) != EOF) {
strSize = strlen(input) + 1;
/* create root node if no current root node */
if (root == NULL) {
if ((newList = (LIST *)malloc(sizeof(LIST))) == NULL) {
printf("Out of memory...");
exit(EXIT_FAILURE);
}
if ((char *)malloc(sizeof(strSize)) == NULL) {
printf("Not enough memory for %s", input);
exit(EXIT_FAILURE);
}
memcpy(newList->str, input, strSize); /*copy string */
newList->count = START_COUNT;
newList->next = NULL;
root = newList;
}
}
return root;
}
/* Prints sinly linked list and returns head pointer */
LIST *PrintList(const LIST *head)
{
int count;
for (count = 1; head != NULL; head = head->next, head++) {
printf("%s %d", head->str, head->count);
}
return head; /* does this actually return the start of head ptr, b/c I want to
return the start of the head ptr. */
}
root has an undefined value, so it won't initialize. The second line of CreateList should be
LIST *root = NULL;
Also, further down there is allocation apparently for the details of the item, but a) the code fails to capture the allocation and save it anywhere, and b) the size of the allocation should be strSize, not the length of the variable itself. There are several ways to fix it, but the most straightforward would be:
newList->str = (char *)malloc(strSize);
if (newList->str == NULL)
The second malloc allocates memory but its return value is not assigned to anything, so that allocated memory is lost.
newList is allocated but not initialized, so using a memcpy to copy memory to newList->str will fail since newList->str points to nothing. Probably you wanted the result of the second malloc to be assigned to newList->str, but you forgot it.
You shouldn't be incrementing head after head = head->next in the for loop. PrintList will return NULL every time since the loop wont stop until head is NULL. Why do you need to return the head of the list you just passed to the function anyway?
Edit:
LIST *current = head;
while (current != NULL) {
printf("%s %d", current->str, current->count);
current = current->next;
}

Resources