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.
Related
In the example below, I created a linked list and I can add numbers successfully. However, at the
end of the execution, the function named "traverse" does not work. How can I fix this error?
Here is my code:
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct node
{
int data;
struct node*prev;
struct node*next;
};
void add( node*head,int number )
{
node*ptr = NULL;
if( head == NULL )
{
head = (node*)malloc(sizeof(node));
head->data = number;
head->next = NULL;
head->prev = NULL;
ptr = head;
}
else
{
ptr->next = (node*)malloc(sizeof(node));
ptr->next->prev = ptr;
ptr = ptr->next;
ptr->data = number;
ptr->next = NULL;
}
}
void traverse( node* head )
{
while( head != NULL )
{
printf("%d ",head->data);
head = head->next;
}
}
int main( void )
{
node *head = NULL;
int number;
char response;
printf("%s\n","Do you want to enter a number in linked list(y/n)?" );
scanf("%c",&response);
while( response == 'y' || response == 'Y' )
{
printf("\nEnter num..> ");
scanf("%d",&number);
add(head,number);
printf("%s\n","Do you want to continue(y/n)?" );
response = getche();
}
printf("\nYour doubly linked list\n");
traverse(head);
getch();
return 0;
}
when "traverse" is called, the console print space like the following image.
If you have decided on C, then continuing from the comments, you are attempting to update a local copy of the pointer head in add(). As mentioned, you have two option, either change the return type of add() to node *add() so you can return ptr and assign as the new head back in main(), or pass the address of head as the first parameter and update the node stored at the original pointer address in add().
You can pass the address of head to add() as follows:
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
(note: since you pass the address of head as a parameter, you must remove one level of indirection from the pointer-to-pointer in add() by dereferncing head (e.g. *head) in order to update the node at the original pointer address. You also need to use the (*head) when further derferencing the pointer with -> due to C operator precedence -- so you get the original pointer address before -> is applied)
Note, the add() function uses a method call Forward-Chaining to add each node to the list in O(1) time. This also means the list will hold the numbers in the reverse order they were entered (last first). You have two options to insert in-order, (1) iterate to the end of the list each time and add a new end node (highly inefficient for large lists, no longer O(1) time, or (2) use another tail pointer that always points to the last node to allow in-order insertions in O(1) time.
You would then call your add() function in main() with
add (&head, number);
Do NOT make things difficult on yourself when testing your list implementation. There is no reason to have to type 'y' then a number and 'y' again before every number you add to your list (that would drive me nuts...). Just add numbers to your list with a loop, you can do input later, e.g.
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
(note: conio.h has been removed and getchar() used to hold the terminal open on windows. Since I'm on Linux, the final getchar() is not compiled as part of my executable)
Your traverse() function will work, but get in the habit of using a separate separate pointer to iterate over you list. This isn't always required, and isn't needed in traverse() since you can use the local copy of head, but always using a temporary pointer to iterate with leave you with the original head address if you need it for use later in your function, e.g.
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
Notice also the delete_list() function added to free() all memory added for your list. You won't always be declaring lists in main() where the memory is freed on exit. Get in the habit of keeping track of the memory you allocate and freeing the memory before your pointer goes out of scope (otherwise, you will create a memory leak)
The full program would be:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *prev, *next;
} node;
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
void delete_list (node *head)
{
node *iter = head;
while (iter) {
node *victim = iter;
iter = iter->next;
free (victim);
}
}
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
Example Use/Output
$ ./bin/llmess
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/llmess
==16661== Memcheck, a memory error detector
==16661== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16661== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==16661== Command: ./bin/llmess
==16661==
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
==16661==
==16661== HEAP SUMMARY:
==16661== in use at exit: 0 bytes in 0 blocks
==16661== total heap usage: 21 allocs, 21 frees, 1,504 bytes allocated
==16661==
==16661== All heap blocks were freed -- no leaks are possible
==16661==
==16661== For counts of detected and suppressed errors, rerun with: -v
==16661== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I have 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.
So I have code that I will show below
struct GraphicElement {
char* fileName;
struct GraphicElement* pNext;
};
struct RasterGraphic {
struct GraphicElement* GraphicElements;
};
AND ALSO
void InsertGraphicElement(struct RasterGraphic* pA)
{
int counter = 1;
int response = 0;
char tempString[256];
struct GraphicElement *newNode = malloc(sizeof(*newNode));
if (newNode == NULL) return;
newNode->fileName = malloc(256 * sizeof(char));
if (newNode->fileName == NULL) return;
newNode->pNext = NULL;
printf("Insert a GraphicElement in the RasterGraphic\nPlease enter the GraphicElement filename: ");
scanf("%s", newNode->fileName);
if (pA->GraphicElements == NULL)
{
pA->GraphicElements = newNode;
printf("This is the first GraphicElement in the list\n");
}
else
{
struct GraphicElement *tempHead = pA->GraphicElements;
while (tempHead->pNext != NULL)
{
tempHead = tempHead->pNext;
counter++;
}
printf("There are %d GraphicElement(s) in the list. Please specify the position (<= %d) to insert at :", counter, counter);
scanf("%d", &response);
if (response == counter) {
tempHead->pNext = newNode;
return;
}
}
return;
}
So as you can see I have a struct definition and then a function to insert a node into the list. I have it so that if its the first node it inserts and tells the user it is the first element in the list. Where I am having a problem is adding more elements. Right now the code has no problems adding new nodes in sequence. The user is asked where they would like to insert into the list. Right now as long as they choose to just add it in sequence it works perfectly. I've tried a lot of things i just cant figure out the logic to loop through and be able to add a node to the middle of the list so an example output would be like this..
List contains 1, 2, 3
the user adds a fourth element 4 but wants to add it to spot 1 which is where the 2 is sitting so the new updated list would look like 1, 4, 2, 3.
There is no magic to inserting anywhere within the linked list, the only real challenge is keeping your pointers straight when handling three conditions:
inserting the first node (or new first node);
inserting a node between at some position between the existing first and last node; and
inserting at the end of the list.
The first case, inserting the first (or new first) node simply requires allocating a new node and either inserting it is the first node in the list, or if the head node already exists, setting newnode->next = head; and head = newnode;
Inserting at the end is not much different, you just iterate to the last node and set last->next = newnode;
The case of inserting a node in between takes a bit more thought. The easiest way to keep your pointers straight is to pull out a pencil and paper and draw out your pointer diagram of your list and then break the list in two where you need to insert the new node, and figure out the steps required before you pickup the keyboard (it will go a lot easier that way)
Nothing fancy is needed, just a block diagram with your next (or your pNext) pointer connecting the nodes. A simple list will two nodes (A and B) will do, e.g.
A B
+------+ +------+
| node | | node |
| next |--> | next |-->NULL
+------+ +------+
Then just break the list at the point where the new node will be inserted, e.g.
A B
+------+ | +------+
| node | / | node |
| next |--> \ | next |-->NULL
+------+ / +------+
|
new
+------+
| node |
| next |-->
+------+
That allows you to visualize the needed steps. Locate node A, set new->next = B;, set A->next = new; (note: you must stop at the node before the location you wish to insert the new node) The result will then be:
A new B
+------+ +------+ +------+
| node | | node | | node |
| next |--> | next |--> | next |-->NULL
+------+ +------+ +------+
When an understanding of exactly what you need to do to handle the insertion, now pick up the keyboard and implement that logic.
Since you will have a single insert function to handle all three cases, as #WhozCraig commented, when handling list operations (or pointers in general where you may need to assign a new block of memory within a function changing the address of the pointer), it helps to pass the address of the pointer to your function (so that your function receives the pointer itself -- instead of a copy of the pointer).
Whether in your case you need to initially allocate for the list wrapper, or in a simple list where you may assign a new node as the first node in the list, passing the address of the pointer allows the change within the function without having to return and assign the return as the new address back in the calling function.
Adding a couple of typedefs gelement_t for your struct GraphicElement and rgraphic_t for struct RasterGraphic types, both to cut down on typing an remove some of the MixedCase style awkwardness, you can think about your InsertGraphicElement function in the following way.
First, your InsertGraphicElement function will either succeed or fail, so choose a meaningful return type that can indicate success or failure. When dealing with lists, returning a pointer to the newly inserted node is helpful and allows a return of NULL in the even of failure, e.g.
gelement_t *InsertGraphicElement (rgraphic_t **pA)
Since you are passing a pointer to pointer to your RasterGraphic struct, you can add data to the struct to make it a bit more useful as a wrapper to your actual list. Adding a node-counter is a convenient way to keep track of the number of nodes in a singly-linked list without having to iterate over the list each time, e.g.
typedef struct RasterGraphic {
size_t nelements;
gelement_t *GraphicElements;
} rgraphic_t;
Within your function you should validate that you have an allocated pointer, and if not, then allocate for the RasterGraphic struct, e.g.
int position = 0;
if (!*pA) { /* if list NULL - allocate new list */
puts ("allocating new list.");
*pA = malloc (sizeof **pA);
if (!*pA) { /* validate every allocation */
perror ("malloc-*list");
exit (EXIT_FAILURE);
}
(*pA)->nelements = 0; /* initialize values */
(*pA)->GraphicElements = NULL;
}
Next you can allocate and validate the new node to add either as the first node or at position, e.g.
gelement_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate */
perror ("malloc-node");
exit (EXIT_FAILURE);
}
Next collect your filename and position information and set node->fileName to a newly allocated block holding the filename entered by the user (a couple of helper-functions make this much easier), e.g.
node->fileName = get_filename_stdin(); /* request filename */
if (!node->fileName) { /* validate */
free (node);
return NULL;
}
node->pNext = NULL; /* set next pointer NULL */
position = get_int_stdin (*pA); /* request position */
You are now at the point within the function where you handle the 3-cases identified at the beginning of the answer. If there is no (*pA)->GraphicElements node yet, you will add the first node (so you don't need to ask for position). If it isn't the first node, and the position requested is 0, you insert as a new first node. (both can be handled in a single case)
If the position requested is greater than zero, then you iterate to the node before the insertion point and insert as indicated with the diagram above and insert the node there.
One approach would be:
gelement_t *p = (*pA)->GraphicElements;
if (!p || position == 0) { /* insert as new head */
node->pNext = p;
(*pA)->GraphicElements = node;
}
else { /* insert at position (default end) */
int n = 0;
while (n < position - 1 && p->pNext) { /* locate node before */
p = p->pNext;
n++;
}
node->pNext = p->pNext; /* set node->pNext to current pNext */
p->pNext = node; /* set current pNext to node */
}
(*pA)->nelements++; /* increment number of elements in list */
Which will handle your insertion based on the user input. All that remains is to:
return node; /* meaningful return to indicate success/failure */
}
(note: when you are comfortable with the list operation logic, it helps to break this function up into several functions that handle the operations individually, like create_list(), create_node() and add_node(). (where you create your RasterGraphic list, your GraphicElement node and finally add that node at a given position in the list -- that is left to you)
Putting it altogether and adding the helper functions, a short example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> /* for PATH_MAX */
typedef struct GraphicElement {
char* fileName;
struct GraphicElement* pNext;
} gelement_t;
typedef struct RasterGraphic {
size_t nelements;
gelement_t *GraphicElements;
} rgraphic_t;
char *get_filename_stdin (void)
{
char filename[PATH_MAX] = "";
char *newname = NULL;
fputs ("enter filename : ", stdout);
if (fgets (filename, PATH_MAX, stdin)) {
size_t len = strlen (filename);
if (len && filename[len-1] == '\n') {
filename[--len] = 0;
if (!len) {
fputs ("error: filename empty.\n", stderr);
return NULL;
}
}
else if (len == PATH_MAX) {
fputs ("error: filename exceeds PATH_MAX\n", stderr);
return NULL;
}
newname = malloc (len + 1);
if (!newname) {
perror ("malloc-newname");
exit (EXIT_FAILURE);
}
memcpy (newname, filename, len + 1);
}
return newname;
}
int get_int_stdin (rgraphic_t *list)
{
char buf[PATH_MAX];
int pos = 0;
if (!list->nelements) {
puts ("inserting as head.");
return 0;
}
fputs ("index to insert: ", stdout);
if (fgets (buf, PATH_MAX, stdin))
if (sscanf (buf, "%d", &pos) != 1 || pos < 0 ||
pos > (long)list->nelements)
return list->nelements;
return pos;
}
gelement_t *InsertGraphicElement (rgraphic_t **pA)
{
int position = 0;
if (!*pA) { /* if list NULL - allocate new list */
puts ("allocating new list.");
*pA = malloc (sizeof **pA);
if (!*pA) { /* validate every allocation */
perror ("malloc-*list");
exit (EXIT_FAILURE);
}
(*pA)->nelements = 0; /* initialize values */
(*pA)->GraphicElements = NULL;
}
gelement_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate */
perror ("malloc-node");
exit (EXIT_FAILURE);
}
node->fileName = get_filename_stdin(); /* request filename */
if (!node->fileName) { /* validate */
free (node);
return NULL;
}
node->pNext = NULL; /* set next pointer NULL */
position = get_int_stdin (*pA); /* request position */
gelement_t *p = (*pA)->GraphicElements;
if (!p || position == 0) { /* insert as new head */
node->pNext = p;
(*pA)->GraphicElements = node;
}
else { /* insert at position (default end) */
int n = 0;
while (n < position - 1 && p->pNext) { /* locate node before */
p = p->pNext;
n++;
}
node->pNext = p->pNext; /* set node->pNext to current pNext */
p->pNext = node; /* set current pNext to node */
}
(*pA)->nelements++; /* increment number of elements in list */
return node; /* meaningful return to indicate success/failure */
}
/* loop over list printing values */
void prn_list (rgraphic_t *list)
{
size_t n = 0;
gelement_t *node = list->GraphicElements;
printf ("\n\n%zu nodes in list\n", list->nelements);
for (; node; node = node->pNext)
printf ("%2zu: %s\n", 1 + n++, node->fileName);
}
/* loop over list freeing memory (pay attention to victim) */
void free_list (rgraphic_t *list)
{
gelement_t *node = list->GraphicElements;
while (node) {
gelement_t *victim = node;
node = node->pNext;
free (victim->fileName);
free (victim);
}
free (list);
}
int main (void) {
rgraphic_t *list = NULL;
puts ("\nNOTE: pressing [Enter] for index - inserts at end!\n"
" [Ctrl+d] at \"filename: \" prompt to end input.\n");
while (InsertGraphicElement(&list)) {} /* create list/insert nodes */
prn_list (list); /* print list */
free_list (list); /* free list */
}
Example Use/Output
$ ./bin/ll_single_insert_ptp
NOTE: pressing [Enter] for index - inserts at end!
[Ctrl+d] at "filename: " prompt to end input.
allocating new list.
enter filename : one
inserting as head.
enter filename : four
index to insert: 1
enter filename : two
index to insert: 1
enter filename : three
index to insert: 2
enter filename : five
index to insert:
enter filename :
5 nodes in list
1: one
2: two
3: three
4: four
5: five
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/ll_single_insert_ptp
==9747== Memcheck, a memory error detector
==9747== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==9747== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==9747== Command: ./bin/ll_single_insert_ptp
==9747==
NOTE: pressing [Enter] for index - inserts at end!
[Ctrl+d] at "filename: " prompt to end input.
allocating new list.
enter filename : one
inserting as head.
enter filename : four
index to insert: 1
enter filename : two
index to insert: 1
enter filename : three
index to insert: 2
enter filename : five
index to insert:
enter filename :
5 nodes in list
1: one
2: two
3: three
4: four
5: five
==9747==
==9747== HEAP SUMMARY:
==9747== in use at exit: 0 bytes in 0 blocks
==9747== total heap usage: 12 allocs, 12 frees, 136 bytes allocated
==9747==
==9747== All heap blocks were freed -- no leaks are possible
==9747==
==9747== For counts of detected and suppressed errors, rerun with: -v
==9747== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
i'm having trouble with building a linked list using two structs
node - contains data and pointer to the next, and list which contains pointer to the head of the list.
i managed to implement it with only the node struct.
i have initialized a struct of a list in the main function
than allocated memory for a list struct using malloc
than i allocated memory for the head which is a pointer to the first node
sent it to another function where there the input,allocating,assigning goes,
but im having hard time to understand how to go through the list without changing the pointer to the head.
and after im done with the nodes and assignment how to get the head pointer to
point back at the start of the list.
should i work with copies? (node *temp) ??
thanks everyone!
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct node
{
int data;
struct node *next;
}node;
typedef struct list
{
struct node *head;
}list;
void main()
{
list *list_a;
list_a = (list*)malloc(sizeof(list));
list_a->head = (node*)malloc(sizeof(node));
assignment(list_a);
}
void assignment(list *list_a)
{
int data;
printf("please enter the numbers:\n(to stop enter ""-1"")\n\n");
scanf("%d", &data);
while (data != -1)
{
list_a->head->data = data;
list_a->head->next = (node*)malloc(sizeof(node));
list_a->head = list_a->head->next;
printf("enter a number:\n");
scanf("%d", &data);
}
}
There are a number of ways to do a linked list, from the mind-numbingly simple add-at-head (which ends up with a list in reverse order) to a fairly standard add-at-tail where you iterate over your nodes to find the end node, and add the new node there. In all cases, it is just a matter of handling your pointers correctly, allocating storage (for both your list parent struct, and each node) and validating all allocations, and then cleaning up after yourself and freeing the used memory when it is no longer needed.
Nested structures where you have a struct holding the head node (and hopefully other useful data to justify the nested approach) are quite common, but there is no need for a parent struct for the list itself. The list address is simply the address of the first node.
When learning lists, it really helps to break the tasks of managing the list down into simple separate functions. This allows you to concentrate (a bit easier) on each list operation singularly. For example, with your list, you will need to:
create/allocate for each node, initialize next pointer NULL and set the data value;
create/allocate for your list, allocating for head and initializing any additional information contained in the list struct;
add nodes to your list, creating the list if needed and adding the node setting data to the proper value and updating any list information needed;
get data out of your list; and
finally freeing the memory for your nodes and your list when it is no longer needed.
In your case, and continuing from my comment to your question, you can declare your structure and typedefs similar to the following:
typedef struct node {
int data;
struct node *next;
} node_t;
typedef struct list {
struct node *head;
size_t n; /* at least make parent hold list size */
} list_t;
Here we a simply added a counter to track the number of nodes in your list as an additional, useful, piece of data to justify the outer stuct. It gives you the node count without having to iterate over the list each time to obtain in (it's just a small efficiency improvement if you need that data). You have the number of nodes in your list with a simple list->n.
Following our list outline, you need a way to create nodes for your list. Whether it is the first node, or last node, you don't care. When you need a node, your create_node function should handle the allocation/validation and initialization. Nothing fancy is needed, e.g.
/** function to create node and set data value to data.
* returns new node on success, NULL otherwise.
*/
node_t *create_node (int data)
{
node_t *newnode = malloc (sizeof *newnode); /* allocate */
if (newnode == NULL) { /* validate/handle error */
perror ("create_node() malloc-newnode");
return NULL;
}
newnode->data = data; /* initialize members */
newnode->next = NULL;
return newnode; /* return pointer to new node */
}
Your create_list function simply needs to allocate for the list struct (and I also have it allocate the first node and initialize the values and pointer 0/NULL). You can have it do whatever you like, e.g. add another parameter passing data for the fist node, etc. I simply have it create the list and first node.
/** function to create list and allocates/initilizes head, set list->n = 0.
* returns new list on success, NULL otherwise.
*/
list_t *create_list (void)
{
node_t *head = NULL;
list_t *list = malloc (sizeof *list); /* allocate list */
if (!list) { /* validate/handle error */
perror ("create_list() malloc-list");
return NULL;
}
head = create_node (0); /* create the first node */
if (!head) /* validate/handle error */
return NULL;
list->head = head; /* initialize list values */
list->n = 0;
return list; /* return list */
}
Your add_node function can be fairly simple, but for purposes here, rather than just stopping if the list is not yet allocated, we will have the add_node function create the list if it doesn't exists and then add the node. This choice has important implications. Since I will handle the case where the list doesn't exist, that means the list address may change within the function. To handle this potential, I must pass the address-of the list as a parameter (e.g. list_t **list instead of simply list_t *list). By having the actual address of the pointer, I can change where the original pointer points and that change will be visible back in the calling function (rather changing where a copy of the pointer points which would not be seen back in the caller).
The function needs to handle two cases (1) "am I the first node?" and (2) "if I'm not the first node, then iterate to end and add there". You can do something similar to:
/** add node to list, create list if list NULL, set node->data to data.
* return new node on success, NULL otherwise.
*/
node_t *add_node (list_t **list, int data)
{
node_t *node;
if (!*list) { /* handle list doesn't exist */
*list = create_list();
if (!*list)
return NULL;
node = (*list)->head; /* (..)-> required by operator precedence */
node->data = data;
}
else { /* list already exists */
node = (*list)->head; /* set node to list->head */
/* iterate over nodes to find last and add node at end */
while (node->next)
node = node->next;
node->next = create_node (data); /* allocate next node */
node = node->next; /* change to new node */
}
(*list)->n++; /* increment number of nodes in list */
return node; /* return node */
}
By doing it this way, I can simply declare the pointer in main() and initialize it NULL and then simply call add_node(&list, x) in main() letting the list functions handle the pointers and allocation.
Your additional list functions are just functions that iterate over each node in the list doing something with the information like printing the list or freeing all the nodes in the list. (pay careful attention to how the node-to-be-freed (e.g. the victim) is handled in the free_list function)
/** print the value of each node in list */
void prn_list (const list_t *list)
{
/* iterate over list printing data value */
for (node_t *node = list->head; node; node = node->next)
printf (" %d", node->data);
putchar ('\n'); /* tidy up with newline */
}
/** free all nodes in list and free list */
void free_list (list_t *list)
{
node_t *node = list->head; /* set node to head */
while (node) { /* iterate over each nod */
node_t *victim = node; /* setting victim to free */
node = node->next; /* change to next node */
free (victim); /* free victim */
}
free (list); /* free list */
}
(note the two different examples of iterating over the nodes using either a for or while loop)
Putting all the pieces together, adding 25 nodes, and printing them out before freeing all memory associated with the list, you could do something like the following:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#if ! defined (_WIN32) && ! defined (_WIN64)
#include <stdlib.h> /* Linux has malloc/free in the stdlib header */
#endif
typedef struct node {
int data;
struct node *next;
} node_t;
typedef struct list {
struct node *head;
size_t n; /* at least make parent hold list size */
} list_t;
/** function to create node and set data value to data.
* returns new node on success, NULL otherwise.
*/
node_t *create_node (int data)
{
node_t *newnode = malloc (sizeof *newnode); /* allocate */
if (newnode == NULL) { /* validate/handle error */
perror ("create_node() malloc-newnode");
return NULL;
}
newnode->data = data; /* initialize members */
newnode->next = NULL;
return newnode; /* return pointer to new node */
}
/** function to create list and allocates/initilizes head, set list->n = 0.
* returns new list on success, NULL otherwise.
*/
list_t *create_list (void)
{
node_t *head = NULL;
list_t *list = malloc (sizeof *list); /* allocate list */
if (!list) { /* validate/handle error */
perror ("create_list() malloc-list");
return NULL;
}
head = create_node (0); /* create the first node */
if (!head) /* validate/handle error */
return NULL;
list->head = head; /* initialize list values */
list->n = 0;
return list; /* return list */
}
/** add node to list, create list if list NULL, set node->data to data.
* return new node on success, NULL otherwise.
*/
node_t *add_node (list_t **list, int data)
{
node_t *node;
if (!*list) { /* handle list doesn't exist */
*list = create_list();
if (!*list)
return NULL;
node = (*list)->head; /* (..)-> required by operator precedence */
node->data = data;
}
else { /* list already exists */
node = (*list)->head; /* set node to list->head */
/* iterate over nodes to find last and add node at end */
while (node->next)
node = node->next;
node->next = create_node (data); /* allocate next node */
node = node->next; /* change to new node */
}
(*list)->n++; /* increment number of nodes in list */
return node; /* return node */
}
/** print the value of each node in list */
void prn_list (const list_t *list)
{
/* iterate over list printing data value */
for (node_t *node = list->head; node; node = node->next)
printf (" %d", node->data);
putchar ('\n'); /* tidy up with newline */
}
/** free all nodes in list and free list */
void free_list (list_t *list)
{
node_t *node = list->head; /* set node to head */
while (node) { /* iterate over each nod */
node_t *victim = node; /* setting victim to free */
node = node->next; /* change to next node */
free (victim); /* free victim */
}
free (list); /* free list */
}
int main (void)
{
list_t *list = NULL; /* just declare list and set pointer NULL */
for (int i = 0; i < 25; i++) /* add 25 nodes to list */
if (add_node (&list, i + 1) == NULL) /* validate each addition */
break;
/* print list content, beginning with number of nodes in list */
printf ("list contains: %lu nodes\n\n", list->n);
prn_list (list); /* followed by each node value */
free_list (list); /* and then delete list */
return 0;
}
Example Use/Output
$ /bin/llsingle_w_parent
list contains: 25 nodes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/llsingle_w_parent
==14749== Memcheck, a memory error detector
==14749== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14749== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==14749== Command: ./bin/llsingle_w_parent
==14749==
list contains: 25 nodes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
==14749==
==14749== HEAP SUMMARY:
==14749== in use at exit: 0 bytes in 0 blocks
==14749== total heap usage: 26 allocs, 26 frees, 416 bytes allocated
==14749==
==14749== All heap blocks were freed -- no leaks are possible
==14749==
==14749== For counts of detected and suppressed errors, rerun with: -v
==14749== 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.
Linked-lists come in all different implementation. You should be aware of a couple of basic distinctions. You have lists that use dummy-node (e.g. dummy head and tail node that do not hold data, but just point to the first/last node in the list). You have circular-linked-lists where the last node points back to the first node (this allows iterating from any node in the list, to any node in the list in a circular fashion across the start and end). So when you look at "linked-list" code, understand that there can be many ways that lists are implemented, all have strengths and weaknesses, so you just have to match your list for the job.
Finally, as specified in the comment, your declaration void main() is incorrect and is an ancient throwback to the early days of windows (like DOS 3.3 and Windows 3.1, Trumpet WinSock, and the Borland Turbo C compiler days) The proper declarations for main are int main (void) and int main (int argc, char **argv) (which you will see written with the equivalent char *argv[]). note: main is a function of type int and it returns a value. See: C11 Standard §5.1.2.2.1 Program startup p1 (draft n1570). See also: See What should main() return in C and C++?
(note: there are some embedded systems that continue to use void main(), but those are the exceptions, not the rule, and are non-compliant with the C-Standard)
Look things over and let me know if you have further questions.
but im having hard time to understand how to go through the list without changing the pointer to the head
Head itself would be the pointer to the first node.
and after im done with the nodes and assignment how to get the head pointer to point back at the start of the list.
You make the new node point to the first node & then shift the pointer pointing to the first node i.e. head to point to the newly added node.
IMPORTANT - You are missing stdlib.h without which malloc can't be used.
Here is a crude version (just for understanding):
while(/*IF YOU WANT TO ADD NODES?*/)
{
if(head == NULL)
{
head = malloc((sizeof(struct node));
head->data = //USER INPUT;
head->next=NULL;
}
else
{
temp = malloc(sizeof(struct node));
temp->data = //USER INPUT;
temp->next = head;
head=temp;
}
}
This entire this can be seen in several steps:
First: head->[data||NULL]
Second: temp->[data||Pointer pointing to 1st node]->(head)[data||NULL]
MOVING HEAD TO MAKE IT POINT TO THE NEW 1st NODE
Third: head->[data||Pointer pointing to Previous 1st node]->[data||NULL]
Isn't it cowardly behaviour to downvote without a valid reason?
#include "header.h"
#define CHECK_NULL(newnode) if(newnode == NULL)\
{ \
printf("\n Memory not allocated...Sorry \n");\
exit(FAILURE);\
}
/* insert first */
node* insert_first(
node* head /* parameter for head of the list*/
,int data/* parameter for data to be inserted */
)
{
/* 1. declare a new node */
node* newnode;
/* 2. allocate memory to new node */
newnode=(node*) malloc(sizeof(node));
/* 3. check */
/* 3.1 if memory is allocated or not*/
CHECK_NULL(newnode)
else{
/* 3.2 else assign data */
newnode->data=data;
/* 3.3 pointer of this new node will point to head */
newnode->next=head;
/* 3.4 head will point to newnode */
head=newnode;
/* 3.5 return head so that you can print it in main */
return head;
}
}
/* insert_after */
node* insert_after(
node*head /*parameter for head of the list*/
,int node_number /* Parameter for knowing the place after which you want to insert the node */
,int data /* parameter for the data to be inserted at the end */)
{
/* 1. declare a new node-same as abv */
node* newnode;
/* 2. declare a current pointer to know the current location you want pointing to */
node* curr;
/* 3. declare count that counts the place where you are right now */
int count=0;
/* 4. allocate memory to the new node */
newnode=(node*)malloc(sizeof(node));
/* 5. check if the memory is allocated */
CHECK_NULL(newnode)
/* 6. assign data to the new node*/
newnode->data=data;
/* 7. assign link to null for the new node */
newnode->next=NULL;
/* 8. if list is empty or number at which you want to insert node is 0 i.e beginning */
if((NULL == head) || (0 == node_number))
{
/*1. let head point to new node */
head=newnode;
/*2. return your head */
return head;
}
/* 9 current pointer points to the same place as head */
curr=head;
/* 10. traverse the linked list till the current->next is not equal to Null */
while(curr->next == NULL)
{
/* 1. increment the counter */
count++;
/* 2. if counter is equal to the place you want to insert node */
if(count == node_number)
{
/*1. next of new node will point to the node next to the current node */
newnode->next=curr->next;
/*2. next of current node will point to the newnode */
curr->next=newnode;
/*3. return your head after inserting the node */
return node;
}
curr=curr->next;
}
/* 11. Insert in end if no. of nodes are not sufficient*/
curr->next=newnode;
}
I am not able to utilize Multi-line macros in my code.
I tried using the do()while(0) solution given over the internet.
But it didn't work out for me
Can you please help me with this? :)
It is a linked list program to insert at the beginning or insert after a certain place.
Here is the error:
I guess that FAILURE is defined like #define FAILURE -1;. ; is extra.
– BLUEPIXY