Append a string from fscanf to linked list in C - c

I want to read a file and put each words in a linked list. When I read the file, the linked list have the good number of nodes but all the node are equal to the last word.
An example, if my text file is :
Hello good sir
My linked list will look like this :
[sir,sir,sir]
And should be like this :
[Hello, good, sir]
My main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef struct NodeTag {
char *data;
struct NodeTag *next;
} Node;
Node *Node_create();
typedef struct ListTag {
struct NodeTag *first;
} List;
List *List_create();
void List_append(List *list, char *str);
void List_print(List *list);
int main(void) {
char word[100];
FILE *file = fopen("file.txt", "r");
if(file == NULL) {
printf("error in opening file\n");
return 1;
}
List *l = List_create();
while(fscanf(file, "%s", word) == 1){
List_append(l, word);
}
return 0;
}
Here are my functions. I removed the destroy and free functions to make it more clear.
Node *Node_create() {
Node *node = malloc(sizeof(Node));
assert(node != NULL);
node->data = "";
node->next = NULL;
return node;
}
List *List_create() {
List *list = malloc(sizeof(List));
assert(list != NULL);
Node *node = Node_create();
list->first = node;
return list;
}
void List_append(List *list, char *str) {
assert(list != NULL);
assert(str != NULL);
Node *node = list->first;
while (node->next != NULL) {
node = node->next;
}
node->data = str;
node->next = Node_create();
}
void List_print(List *list) {
assert(list != NULL);
printf("[");
Node *node = list->first;
while (node->next != NULL) {
printf("%s", node->data);
node = node->next;
if (node->next != NULL) {
printf(", ");
}
}
printf("]\n");
}
If I do something like this, it will work properly. So I guess I append only the pointer of word so its pointing to the same place again and again ?
List_append(l, "test1");
List_append(l, "test2");
Output :
[test1, test2]

Notice that in main, you have one buffer storing strings:
char word[100];
You pass word as a parameter to your List_append method, during which you write
node->data = str;
This means that all of the nodes are pointing back to the word buffer in main for their string, so all of them will display the same string.
To fix this, you need to duplicate the buffer somewhere. I'd recommend doing something like this:
node->data = strdup(str);
There may be other issues in the code, but this is certainly something that you'll need to fix before you move on. Try updating this and see if it resolves your issue. As #Sean Bright points out, it seems like you're also overwriting the wrong string pointers when you do an append, so you'll probably need to fix that as well.
Hope this helps!

Inside of List_append, you're doing this:
node->data = str;
That saves a pointer to the data that was passed in as str. You call List_append(l, word) in main, meaning you're passing it the same piece of memory every time, so each list member points to the same piece of memory.
You instead need to do this in List_append:
node->data = strdup(str);
This copies the string into a newly allocated buffer. Just be sure to free it when you clean up.

Related

Unnecessary Changes being made when appending to linked list

A I have managed to get the linked list to function in the sense that it can create a list store variables in them, but now I have run into another issue that I've never been able find the solution to. Any time I run it through a list of variables I want stored it will run through the list and create the right number of nodes, but the string variable keeps getting changed after each append.
For example if I run:
"Dog" "cat" "house"
Instead of the desired output:
Dog
cat
house
It produces
house
house
house
I'm unsure why it keeps doing it and I can't seem to pin where the head node string is being altered except for the first instance in which the list is empty and thus needs to assign a new head.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#define EMPTY NULL;
typedef struct listnode{
struct listnode* next;
char* fileName;
} listnode;
struct listnode* head;
//This section of code will be dedicated to the creation and management
//of the listnode functions
listnode* createNode(char* str, listnode* next){
listnode* tempo;
tempo = (listnode*)malloc(sizeof(struct listnode));
if(tempo == NULL){
printf("Error creating space for new node.\n");
exit(0);
}
tempo->fileName = str;
tempo->next = next;
return tempo;
}
listnode* append(listnode* head, char* str){
listnode* temp;
listnode* curr;
temp = createNode(str, NULL);
if(head == NULL){
head = temp;
return head;
}
else{
curr = head;
while(curr->next != NULL){
curr = curr->next;
}
curr->next = temp;
return head;
}
}
void printNames(listnode* head){
listnode* curr= head;
while(curr !=NULL){
printf("%s \n", curr->fileName);
curr = curr->next;
}
}
void list_free(listnode* head){
listnode* current;
listnode* temp;
if(head != NULL){
current = head->next;
if(head !=NULL){
current = head -> next;
head ->next = NULL;
while(current != NULL){
temp = current -> next;
free(current);
current = temp;
}
}
}
free(head);
}
int main(int argc, char **argv){
char *current_dir = NULL;
DIR *direct_ptr = NULL;
struct dirent *dir_ptr = NULL;
unsigned int fileNum = 0;
int c;
listnode* head = NULL;
current_dir = getenv("PWD");
if(NULL == current_dir){
printf("\n Error: Couldn't grab current directory.\n");
return -1;
}
direct_ptr = opendir((const char*)current_dir);
if(NULL == direct_ptr){
printf("\n Error: couldn't open current directory\n");
return -1;
}
for(fileNum=0; NULL != (dir_ptr = readdir(direct_ptr)); fileNum++){
if(dir_ptr->d_name[0] != '.'){
head = append(head, dir_ptr->d_name);
}
}
printNames(head);
}
In C, arrays (such as strings) are not passed by value. They are instead passed by pointer. When you specify the a char array as a function parameter, the array decays to a pointer.
Therefore, you must either
ensure that the string that the listnode struct member filename points to remains valid and is not overwritten, or
store a copy of the string in the listnode struct.
In order to copy a string, you can use the function strcpy. However, you must then either allocate enough space for the char array in the listnode struct or you must use dynamic memory allocation (such as malloc) and store a pointer to the dynamically allocated memory.

Linked list of strings has the same strings for each node

I'm trying to make a linked list, where each node stores a string, but I'm having a problem where each node ends up storing the same exact string in every single node. At the end of main(), I print out the word stored in each node, and it always just repeats the last string entered for the entire list.
I don't have any clue what is happening, because if I make it a string of characters it works perfectly fine, where each character is stored in the correct node.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct wordnode {
char *word;
struct wordnode *next;
};
struct wordnode *link = NULL;
void addword(char *aword);
int main(void) {
char *aword;
int i;
for(i = 0; i < 10; i++) {
scanf(" %s", aword);
addword(aword);
}
printf("\n");
for(; link != NULL; link = link->next) {
printf("|%s ", link->word);
}
printf("|\n");
return 0;
}
void addword(char *aword) {
struct wordnode *cur, *prev, *new_node;
new_node = malloc(sizeof(struct wordnode));
new_node->word = aword;
for(cur = link, prev = NULL; cur != NULL; prev = cur, cur = cur->next) {
;
}
new_node->next = cur;
if(prev == NULL) {
link = new_node;
} else {
prev->next = new_node;
}
}
There are many problems in the code. Some of them are already mentioned. The code will be something like this. Explanation at the end of the code.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define STR2(x) #x
#define STR(X) STR2(X)
#define MAXWORD 10
#define MAXWORDLEN 20
struct wordnode {
char *word;
struct wordnode *next;
};
struct wordnode* addword(char *aword, struct wordnode *link);
void printList(struct wordnode*link);
void freeList(struct wordnode *link);
int main(void) {
char aword[MAXWORDLEN+1];
struct wordnode *link = NULL;
for(size_t i = 0; i < MAXWORD; i++) {
if( scanf("%" STR(MAXWORDLEN) "s", aword[i]) == 1 ){
link = addword(aword, link);
}
else{
fprintf(stderr, "%s\n","Error in input" );
exit(1);
}
}
printList(link);
freeList(link);
return 0;
}
void printList(struct wordnode*link){
while(link){
printf("%s \n", link->word);
link = link->next;
}
}
void freeList(struct wordnode *link){
struct wordnode *temp;
while(link){
temp = link;
link = link->next;
free(temp);
}
}
struct wordnode* addword(char *aword, struct wordnode *link) {
struct wordnode *new_node = malloc(sizeof(struct wordnode));
if( new_node == NULL){
fprintf(stderr, "%s\n", "Error in malloc");
exit(1);
}
new_node->word = strdup( aword );
if( new_node->word == NULL){
fprintf(stderr, "%s\n", "Error in strdup" );
exit(1);
}
new_node->next = NULL;
if( link == NULL){
return new_node;
}
struct wordnode *cur = link;
while( cur->next != NULL ){
cur = cur -> next;
}
cur->next = new_node;
return link;
}
You wanted to store some string (nul terminated char array) and then you wanted to add them in the list. Also from your example implementation you were trying to add it in the list at the tail.
To sum up -
scanf demands a pointer to some memory where it can store inputted data. But yours were uninitialized.
secondly the way you were copying strings, it was just a shallow copy (you were making it point to some already existing memory). You need copy it either using strdup or malloc - memcpy or malloc-strcpy.
In case POSIX strdup() is not available, you can use what Jonathan Leffler mentioned.
Here you can see that we have freed the allocated memory using freeList() function. When you are done working with the memory you allocated- free the memory.
Don't cast the return value of malloc.
Also check whether the malloc is successful checking it's return value.
You have used the list head as the global variable. It is not needed here.
char * aword is not initialized and located. It should be:
char aword[100];
(100 is just a number, size of array. You can replace it with any number what you want)
You have not allocated memory for the string. As a result aword would contain garbage value and passing this to scanf is a undefined behavior. Lets say aword has 0x7fffe4e0cdf0 and your scanf stores the string at the address 0x7fffe4e0cdf0 and pass this address to the addword function and your structure member word is also updated with the same value. The next scanf also stores the new value in the same memory pointed by aword and passes to the function. As a result word in all the linked lists are pointing to the same memory location. The ideal solution is to allocate memory for each string being scanned and pass it to the '`addword' function.

C: fgets usage for building a linked list of char*

Am I using fgets() incorrectly?
I am trying to build a linked list of strings (char *) adding each new line to the end of the LL. I am reading these lines from a file, but for some reason every line gets overwritten by the current line being processed, only when using fgets() inside the while loop, but the add function seems to be receiving each line correctly.
If I add lines individually in main() there are no problems.
Here is a sample input file:
input.txt:
This life, which had been the
tomb of his virtue and of his
honour, is but a walking
shadow; a poor player, that
struts and frets his hour upon
the stage, and then is heard
no more: it is a tale told by an
idiot, full of sound and fury,
signifying nothing.
--William Shakespeare
The code:
#include <stdio.h> //printf, fopen
#include <stdlib.h> //exit, EXIT_FAILURE
#include <string.h> //strlen
struct node {
char *line;
struct node *next;
};
void print(struct node *node);
void add(struct node **head, char *newLine) {
//printf("%s", newLine);
struct node *new_node = (struct node *)malloc(sizeof(struct node));
struct node *curr = *head;
new_node->line = newLine;
new_node->next = NULL;
if (*head == NULL) {
*head = new_node;
} else {
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = new_node;
}
print(*head);
}
void print(struct node *node) {
printf("\n");
while (node != NULL) {
printf("%s\n", node->line);
node = node->next;
}
}
int main(int argc, char *argv[16]) {
char newLine[81];
struct node *head = NULL;
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
printf("ERROR: file open failed");
exit(EXIT_FAILURE);
}
while (fgets(newLine, 81, fp)) {
add(&head, newLine);
}
add(&head, "why");
add(&head, "does");
add(&head, "this");
add(&head, "work??");
fclose(fp);
print(head);
return 0;
}
Could someone please explain to me what is happening? I have been banging my head against a wall for too long. There are already some commented print statements I have been attempting to use, unsuccessfully for debugging.
Your problem is in the add() method.
It keeps adding the same buffer pointer to the list.
You need to copy the buffer in your list to newly allocated space, ie.
node->line needs to be malloced, too, and the newLine copied into it.
Don't forget to malloc (strlen (newLine) + 1).
You have one buffer where you store the input. And you pass a pointer to the first element of this single buffer when adding nodes. That means the string pointer in all nodes will point to the same single buffer. Which will in the end contain the last string you read.
The simplest solution is to make the string in the node-structure an array, and copy the string into it.
Another solution is to allocate memory for the string dynamically (remembering the terminating null character) and again copy the string into that memory.
The difference when using constant string literals is that each of the strings will be a different array.
You must allocate memory for each line. As currently coded, all nodes point to the local array in main() whose the contents is overwritten by each call to fgets().
Also note that each line added to the list contains a terminating newline which you probably should get rid of before the call.
Here is a corrected version:
#include <stdio.h> // printf, fopen
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string.h> // strlen, strdup
struct node {
char *line;
struct node *next;
};
void print(struct node *node);
void add(struct node **head, char *newLine) {
//printf("%s", newLine);
struct node *new_node = malloc(sizeof(struct node));
struct node *curr = *head;
new_node->line = strdup(newLine);
new_node->next = NULL;
if (*head == NULL) {
*head = new_node;
return;
}
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = new_node;
print(*head);
}
void print(const struct node *node) {
printf("\n");
while (node != NULL) {
printf("%s\n", node->line);
node = node->next;
}
}
int main(int argc, char *argv[16]) {
char newLine[81];
struct node *head = NULL;
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
printf("ERROR: file open failed");
exit(EXIT_FAILURE);
}
while (fgets(newLine, sizeof newLine, fp)) {
newLine[strcspn(newLine, "\n")] = '\0'; // strip the newline if present
add(&head, newLine);
}
add(&head, "why");
add(&head, "does");
add(&head, "this");
add(&head, "work??");
fclose(fp);
print(head);
return 0;
}

program crashes while iterating through a linked list

I am supposed to create a linked list with the following properties:
struct node{
char *word;
int times;
struct node *next;
}head;
I have to read each word from a file, check if it exists in the list, and if it doesn't, i should add it. If it already exists i need to increase times by one. I use the following code to accomplish that:
void append(char *wrd, struct node *start){
struct node *current = start;
while(current->next != NULL){
current = current->next;
}
current->next = malloc(sizeof(struct node));
strcpy(current->word, wrd);
current->times = 1;
current->next->next = NULL;
}
int iterate(char *wrd, struct node *start){
struct node *current = start;
while(current != NULL){
if(strcmp(current->word, wrd)==0){
current->times++;
return 1;
}
current = current->next;
}
return 0;
}
void read_file(struct node *start){
FILE *fp;
char wrd[20];
puts("give file name and format(max 19 characters)");
gets(wrd);
fp = fopen((const char*)wrd, "r");
fscanf(fp, "%s", wrd);
start->word = malloc(strlen(wrd)+1);
strcpy(start->word, wrd);
while(fscanf(fp, "%s", wrd) != EOF){
if(!iterate(wrd, start)){
append(wrd, start);
}
}
}
void print_data(struct node *start){
struct node *current = start;
while(current->next != NULL){
printf("word: %s , times: %d", current->word, current->times);
}
}
int main(int argc, char *argv[]) {
struct node *start = &head;
read_file(start);
return 0;
}
append takes a word, creates a new node containing it, and adds the node to the list.
iterate takes a word and searches the list for a match. If the word already exists within the list, then times is increased by one. 0 is returned if no match was found and 1 in the opposite case.
read_file initializes the head node, reads the file and calls the above functions for each word it reads.
Let's say i have a text file containing the following words:
hello hey world hello
world this is supposed to work
but it does not
The program successfuly runs for the first 3 words and creates the nodes. When the match hello is found the program crashes. I've determined that the error lies in iterate, but i can't figure out what's causing it. Any help is greatly appreciated.
First things first, I don't understand why you can confidently say that your program has an error in the iterate function.
You haven't allocated any memory to the word pointer before strcpy().
I would suggest doing this in your append() function:
void append(char *wrd, struct node *start){
struct node *current = start;
if(current == NULL){
start = (struct node*)malloc(sizeof(struct node));
start->word = (char *)malloc((strlen(wrd) + 1)*sizeof(char));
strcpy(start->word, wrd);
start->times = 1;
start->next = NULL;
return;
}
while(current->next != NULL){
current = current->next;
}
current->next = (struct node*)malloc(sizeof(struct node));
current->next->word = (char*)malloc((strlen(wrd) + 1)*sizeof(char)); //add this line
strcpy(current->next->word, wrd); //not current->word
current->next->times = 1; //not current->times
current->next->next = NULL;
}
Notice here that in the append function, you did not check if the list is already empty or not. The if block is to do the same. Also notice the mistake you made while using strcpy(). You wanted to copy the new word in the new pointer, but were doing it in the pointer which is the parent of the new node.
Now, you read_file() function will look a lot simpler!
void read_file(struct node *start){
FILE *fp;
char wrd[20];
puts("give file name and format(max 19 characters)");
gets(wrd);
fp = fopen((const char*)wrd, "r");
while(fscanf(fp, "%s", wrd) != EOF){
if(!iterate(wrd, start)){
append(wrd, start);
}
}
}
I don't think your iterate function needs an update, but print_data() surely does:
void print_data(struct node *start){
struct node *current = start;
while(current != NULL){
printf("word: %s , times: %d", current->word, current->times);
current = current->next;
}
}
Seems like the only function not having any error was iterate(). Happens when you play with pointers! :P

Linked List in C

I am having some trouble running this linked list implementation (containing words as data). Problem is when I try to print the words (that I inserted) in the Linked List,I get nothing. What am I doing wrong, breaking my head over this? I hope it's not something silly. Anyway here's the code -
typedef struct node
{
void *data;
struct node *next;
} NODE;
NODE *new_node(void *data)
{
NODE *new = malloc(sizeof(NODE));
if(new)
{
new->data = data;
new->next = NULL;
return new;
}
else
{
return NULL;
}
}
void print_list(NODE *head, void (print_fn) (void*))
{
if(head && head->next)
{
while(head->next)
{
if(print_fn)
print_fn(head->data);
else
printf("Word: %s\n", (char *)head->data);
head = head->next;
}
}
return;
}
void append(NODE **head, NODE *node)
{
NODE *tmp = *head;
if(tmp && node)
{
while(tmp->next)
tmp = tmp->next;
(*head)->next = node; /*add as last node*/
}
return;
}
NODE *create_list()
{
FILE *dict_file = fopen("trial.txt", "r");
if(dict_file)
{
NODE *head = new_node(NULL);
if(!head) return NULL;
char word[20];
int first = TRUE;
memset(word, '\0', 20);
while(fgets(word, sizeof(word), dict_file) != NULL )
{
if(first)
{
head->data = (void*)word;
first = FALSE;
}
else
{
append(&head, new_node((void*)word));
}
}
fclose(dict_file);
return head;
}
else
{
printf("ERROR: File not found");
return NULL;
}
}
int main(int argc, char *argv[])
{
NODE *head = create_list();
if(!head)
{
printf("ERROR: Either malloc() failed or data not found\n");
return FALSE;
}
else
{
print_list(head, NULL);
return TRUE;
}
}
This has become quite a long answer. Don't take this personally, but you have made quite a few rookie mistakes. I have met with many people at university whom I helped to learn the C language and programming in general, so I've got used to notice these kinds of things.
The important issues I could find
You assign a pointer to a stack variable to words
This is completely bad, because that value will be overwritten once the execution gets out of the function that creates it. Solution: copy the content of that variable into a heap variable.
Your append function is faulty
It adds the appended element to the second place instead of the last place. Note that you don't need the return at the end either. There is also no point in requiring a double-pointer as input to the append method. Also, after assigning head to tmp, it is futile to check tmp against NULL as well, since it won't be NULL if head isn't NULL. Also, I recommend checking the new node against NULL as well. If it is NULL, this saves you from iterating over the entire collection.
The create_list function is sub-optimal
Fist, the distinction between the first and the other cases are futile. Introducing another pointer (called current in my code) will eliminate the need to check whether it is the first or not. Next, you always call the append function on the head, thus you always need to iterate over the entire collection. This can be also optimized by introducing the current variable. (Which, at start, should be assigned the value of head.)
The print_list function is errornous
It doesn't print anything if there is only one node. It also redundantly checks the pointers for null. (The beginning of the loop checks it, too.) The return statement at the end of this void function is also unnecessary.
You should free up memory when you don't use it
#Baltasarq wrote a nice clear function in his answer, you should use it. :)
Not serious errors, but you should be aware of them
You should not use void* instead of char*
If you know that the data member of the NODE structure is going to store characters, why do you use void*? It is bad practice! (Unless you have a good reason, of course.)
Using the new word as a variable name makes your code not compliant with C++. Thus, I recommend against it.
Adopt a better coding style, please - it will make your code so much easier to read
Inconsistency: if in print_list you don't allocate a new variable to go through the collection (like you do with the tmp variable in append) then it is misguiding to name the parameter as head. (I renamed it to node in my code.)
Here is the fixed code
(Note that there may be small syntax errors because I typed the code into the browser without actually testing it.)
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct node
{
void *data;
struct node *next;
} NODE;
NODE *new_node(void *data)
{
NODE *newNode = (NODE*)malloc(sizeof(NODE));
if (newNode)
{
newNode->data = data;
newNode->next = NULL;
return newNode;
}
return NULL;
}
void append(NODE *head, NODE *node)
{
if (head && node)
{
NODE *tmp = head;
while (tmp->next)
tmp = tmp->next;
tmp->next = node; /* add as last node */
}
}
void print_list(NODE *node, void (print_fn) (void*))
{
while (node)
{
if (print_fn)
print_fn(node->data);
else
printf("Word: %s\n", (char *)node->data);
node = node->next;
}
}
NODE *create_list()
{
FILE *dict_file = fopen("trial.txt", "r");
if (dict_file)
{
NODE *head = NULL;
NODE *current = head;
char word[20];
memset(word, '\0', 20);
while (fgets(word, sizeof(word), dict_file))
{
// Creating a variable on the heap
char *data = calloc(sizeof(word) + 1, sizeof(char));
// Copying the contents of words to it
strcpy(data, word);
append(current, new_node((void*)data));
if (current->next)
current = current->next
}
fclose(dict_file);
return head;
}
else
{
printf("ERROR: File not found");
}
return NULL;
}
int main(int argc, char *argv[])
{
NODE *head = create_list();
if (!head)
{
printf("ERROR: Either malloc() failed or data not found\n");
}
else
{
print_list(head, NULL);
}
return 0;
}
Be careful, since malloc() and derivatives can answer with a NULL pointer when there is not enough memory. In fact, take into account that you'll also need a clear() method that free's all the data, as well as the nodes themselves.
void clear(NODE *node)
{
NODE * temp = NULL;
while( node != NULL ) {
temp = node->next;
free( node->data );
free( node );
node = temp;
}
}
Your main() function should return an exit code to the operating system of EXIT_SUCCESS or EXIT_FAILURE, instead of TRUE or FALSE.
int main(int argc, char *argv[])
{
NODE *head = create_list();
int toret = EXIT_SUCCESS;
if(!head)
{
printf("ERROR: Either malloc() failed or data not found\n");
toret = EXIT_FAILURE;
clear( head );
}
else
{
print_list(head, NULL);
clear( head );
}
return toret;
}

Resources