Using fgets and linked lists - c

I am trying to get user input from fgets() and save the entry into a linked list but it isn't saving the entry into the linked lists, but if I directly put it in the call, it does. ex. add_to_list(&list, "hello"); How do use fgets to save into a character array (called word), which I can stick into add_to_list call?
void
add_to_list(struct linked_list *list, char *x)
{
struct node *n = malloc(sizeof *n);
n->data = x;
n->next = NULL;
if (list->head == NULL)
list->head = n;
if (list->tail != NULL)
list->tail->next = n;
list->tail = n;
}
int
main(void)
{
struct linked_list list = { .head = NULL, .tail = NULL };
char word[50];
do {
printf("Enter string: ");
fgets(word, 50, stdin)
add_to_list(&list, word);
} while (word[0] != '\n');
//add_to_list(&list, "hello");
print_list_rec(&list);
free_list(&list);
return 0;
}

Here is the problem:
n->data = x;
This statement assigns the pointer to the buffer to the data pointer of the list. However, this is the same pointer in all calls. Moreover, the buffer is in the automatic storage, because word is a local variable.
In order to fix this problem you need to add copying of the string into dynamically allocated buffers. Use strlen to decide how much memory you need, strcpy to copy the data, and don't forget to free the string after you are done with the struct.
size_t len = strlen(x);
n->data = malloc(len+1);
strcpy(n->data, x);
This goes inside free_list:
free(n->data);
free(n);

Related

Storing strings from pointers in Linked lists

Recently started to practice linked lists. I am aware of the basic algorithm and concept and thought of implementing LL to store a bunch of strings which are input by the user.
But apparently I keep getting Segmentation fault.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _node{
char *s;
struct _node *next;
}
node;
int main()
{
node *head = NULL;
int a = 0;
char ch;
char *str = malloc(10);
do
{
printf("\nDude %i:", a);
fgets(str, 10, stdin);
node *n = malloc(sizeof(node));
if(n == NULL)
{
printf("\ninsufficient memory");
return 1;
}
if(a == 0)
{
strcpy(n->s, str);
n->next = NULL;
head = n;
}
else
{
strcpy(n->s, str);
n->next = head;
head = n;
}
a++;
printf("\n continue?(y/n): ");
scanf("\n%c", &ch);
}while(ch == 'y');
for(node *temp = head; temp != NULL; temp = temp -> next)
{
printf("\n%s", temp->s);
}
return 0;
}
I do understand that my logic/code is flawed somewhere since I am touching memory I should not touch but cannot seem to point out where since it is my first time dealing with linked lists.
When you are malloc'ing space for the struct, you are only allocating space for the pointer to the string in your _node struct. You need to allocate some memory where you store the string and point the pointer s to it, before you do the strcpy.
i.e.
n->s = malloc(sizeof(char)*100);
Remember that you also need to have a strategy to de-allocate this memory.
As the others hinted, these sort of errors are usually easily caught by looking/debugging with gdb. Remember that it's useful to compile with the -g flag to get useful debugging info.
The reason you catch a ''Segmentation fault' is because you don't allocate memory for s variable of struct node before copying actual string: strcpy(n->s, str).
So, allocate memory for s:
n->s = (char *) malloc(10 * sizeof(char));
Note that you cannot write anything into an unallocated space, so you need to call malloc for the string in each node.
If the string lengths are fixed, then you can specify the length in the definition of struct node to avoid the malloc problem.
Also, it is suggested to always free the objects that will no longer be referenced.
With a few revisions, the codes below may be helpful:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 10
typedef struct node {
char str[LEN];
struct node *next;
} Node;
int main() {
Node *head = NULL;
int n = 0;
char c = 'y';
while (c == 'y') {
Node *node = malloc(sizeof(Node));
printf("Node #%d: ", n);
scanf(" ");
/* Store the string directly into the node. */
fgets(node->str, 10, stdin);
/* Remove the newline character. */
node->str[strcspn(node->str, "\n")] = 0;
node->next = head;
head = node;
++n;
printf("Continue? (y/N): ");
scanf("%c", &c);
};
Node *curr = head;
while (curr != NULL) {
printf("%s\n", curr->str);
Node *temp = curr;
curr = curr->next;
/* Remember to free the memory. */
free(temp);
}
return 0;
}

Tokenizing input file into linked list

I'm attempting to tokenize an input file and store its individual words within a linked list organized by word count. I've been struggling with storing the tokenized string into a node, and am struggling to understand what is incorrect in my tokenizing/inserting process. Currently, when printing the stored strings out, the first letter of each string is truncated off, and there is seemingly random garbage and the end of each string. I have tried the following to fix my error:
Null-terminating each string after tokenization (I've left that in
my program as it appears to be correct)
Using strncpy() instead of new_word->str = str;
Passing a pointer to the tokenized string to my insert function,
instead of just passing the string itself.
Below is my code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <strings.h>
typedef struct word{
int length;
char *str;
struct word *left;
struct word *right;
struct word *down;
}word;
void print_list(word **head){
word *temp_traverse = *head;
word *temp_down;
for( ; temp_traverse!=NULL; temp_traverse = temp_traverse->right){
temp_down = temp_traverse;
for( ; temp_down!=NULL; temp_down = temp_down->down){
printf("Count: %d, String: %s\n", temp_down->length, temp_down->str);
}
}
}
int is_empty(word **head, word **tail){
if((*head == NULL)||(*tail == NULL))
return 1;
return 0;
}
void insert(word **head, word **tail, word *new_word){
if(is_empty(head, tail)){
(*head) = new_word;
(*tail) = new_word;
return;
}
if((new_word->length)<((*head)->length)){
new_word->right = (*head);
(*head)->left = new_word;
(*head) = new_word;
return;
}
word *temp = *head;
while(((temp->right)!=NULL) && ((temp->length)<(new_word->length)))
temp = temp->right;
if((temp->length) == (new_word->length)){
while(temp->down != NULL)
temp = temp->down;
temp->down = new_word;
return;
}
if(temp->right == NULL){
word* last = (*tail);
last->right = new_word;
new_word->left = last;
(*tail) = new_word;
return;
}
word* next = temp->right;
temp->right = new_word;
next->left = new_word;
new_word->left = temp;
new_word->right = next;
return;
}
void create(word **head, word **tail, char **str){
word *new_word = (word*)malloc(sizeof(word));
int length = strlen(*str);
if(new_word == NULL){
fprintf(stderr, "Error creating a new word node.\n");
exit(0);
}
new_word->str = (char*)malloc(sizeof(*str));
strncpy(new_word->str, *str, length);
//new_word->str = *str;
new_word->length = length;
printf("%s ", new_word->str); //test print
new_word->left = NULL;
new_word->right = NULL;
new_word->down = NULL;
insert(head, tail, new_word);
return;
}
void tokenize(word **head, word **tail, char words_buffer[]){
char *cur;
cur = strtok(words_buffer, " .,;()\t\r\v\f\n");
*cur++ = '\0';
create(head, tail, &cur);
/* tokenize the next string and reset the "duplicate" variable */
while((cur = strtok(NULL, " .,;()\t\r\v\f\n")) != NULL){
//cur = strtok(NULL, " .,;()\t\r\v\f\n");
*cur++ = '\0';
if(cur){
create(head, tail, &cur);
}
}
}
int main(int argc, char *argv[]){
FILE *fp;
word *head = NULL;
word *tail = NULL;
/*if(argc<3){
printf("Failure: not enough arguments");
return -1;
}*/
fp = fopen(argv[1], "r");
fseek(fp, 0, SEEK_END);
char words_buffer[ftell(fp)+1];
fseek(fp, 0, SEEK_SET);
if(fp==NULL){
printf("Failure: unreadable file");
return -1;
}
while(fgets(words_buffer, sizeof(words_buffer), fp)){
if(strlen(words_buffer)>1)
tokenize(&head, &tail, words_buffer);
}
//print_list(&head);
fclose(fp);
return 0;
}
I've left my test string printing for your reference. You will also notice that I don't use print_list right now, as I have yet to store strings correctly.
Because of the garbage at the end, I am assuming I am either incorrectly using the pointer to the string, or am malloc()ing too much space. As for the truncation, I'm not sure, but I presume it is related to my *cur++ = '\0'; line.
Any help is greatly appreciated, thanks for taking the time to take a look.
You are not copying the whole string with your strncpy().
In fact, you are copying one character too few when you obtain the length with:
int length = strlen(*str);
As stated in the strncpy() manpage:
Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.
So make sure that when you use functions operating on null-terminated strings, such as most of the standard library str*() functions, that you account for the '\0' terminator with:
int length = strlen(*str) + 1;
Also, as an aside, the void * returned by malloc() is implicitly converted to any object pointer type, so instead of:
word *new_word = (word*)malloc(sizeof(word));
you should simply use:
word *new_word = malloc(sizeof(word));
or even better:
word *new_word = malloc(sizeof *new_word);
to avoid errors caused by changing the pointer type in the declaration but not the malloc() call.
The sizeof operator doesn't evaluate non-variable-length array expressions, so this is a much more reliable way to obtain an object's size.
EDIT
As to the first character of each string missing, I would assume that is due to:
*cur++ = '\0';
as that just uselessly sets cur[0] to '\0', and then increments the pointer; the string now starts at the second letter of your word.

Creating Dynamically Allocated Strings from a file in C

I am having some issues with dynamically allocating a string for a node in a tree. I have included my node structure below for reference.
struct node
{
char *string;
struct node *left;
struct node *right;
};
typedef struct node node;
I am supposed to read words from a text file and then store those words into a tree. I am able to store char arrays that have been defined, such as char string[20] without problems, but not strings that are supposed to be dynamically allocated.
I am only going to post the code I am using to read my file and try to create the dynamically allocated array. I have already created the file pointer and checked that it is not NULL. Every time I try to run the program, it simply crashes, do I need to try and read the words character by character?
//IN MAIN
node *p, *root ;
int i;
int u;
root = NULL;
char input[100];
while(fscanf(fp, "%s", &input) != EOF)
{
//Create the node to insert into the tree
p = (node *)malloc(sizeof(node));
p->left = p->right = NULL;
int p = strlen(input); //get the length of the read string
char *temp = (char*) malloc(sizeof(char)*p);
//malloc a dynamic string of only the length needed
strcpy(local, input);
strcpy(p->word,local);
insert(&root, p);
}
To be completely clear, I only want advice regarding the logic of my code, and only would like someone to help point me in the right direction.
You are invoking many undefined behaviors by
passing pointer to object having wrong type to scanf(). i.e. In fscanf(ifp, "%s", &input), char(*)[100] is passed where char* is expected
accessing out-of-range of allocated buffer when storeing terminating null-character in strcpy(local, input);
using value of buffer allocated via malloc() and not initialized in strcpy(curr->word,local);
Your code should be like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct node_t {
struct node_t* left, *right;
int count;
char* word;
} node;
void insert(node ** tree, node * item);
int main(void) {
FILE* ifp = stdin;
node * curr, * root;
int i;
int u;
root = NULL;
char input[100];
/* you should specify the maximum length to read in order to avoid buffer overrun */
while(fscanf(ifp, "%99s", input) != EOF)
{
//Create the node to insert into the tree
curr = malloc(sizeof(node));
if(curr == NULL) /* add error check */
{
perror("malloc 1");
return 1;
}
curr->left = curr->right = NULL;
curr->count = 1;
int p = strlen(input); //get the length of the read string
char *local = malloc(sizeof(char)*(p + 1)); /* make room for terminating null-character */
if (local == NULL) /* add error check again */
{
perror("malloc 2");
return 1;
}
//malloc a dynamic string of only the length needed
//To lowercase, so Job and job is considered the same word
/* using strlen() in loop condition is not a good idea.
* you have already calculated it, so use it. */
for(u = 0; u < p; u++)
{
/* cast to unsigned char in order to avoid undefined behavior
* for passing out-of-range value */
input[u] = tolower((unsigned char)input[u]);
}
strcpy(local, input);
curr->word = local; /* do not use strcpy, just assign */
insert(&root, curr);
}
/* code to free what is allocated will be here */
return 0;
}
//Separate insert function
void insert(node ** tree, node * item)
{
if(!(*tree))
{
*tree = item;
return;
}
if(strcmp(item->word,(*tree)->word) < 0)
insert(&(*tree)->left, item);
else if(strcmp(item->word,(*tree)->word) > 0)
insert(&(*tree)->right, item);
/* note: memory leak may occur if the word read is same as what is previously read */
}

inserting strings into linked list is not working correctly

I have a program that is supposed to take an input file and extract the strings from it and add it to a linked list. I do not think I am adding the strings to the linked list correctly and i cannot seem to find the right way to. When the program executes it goes into an infinite loop for some reason.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list {
char *string;
struct list *next;
};
typedef struct list LIST;
int main() {
FILE *fp;
char line[128];
char file_name[20];
LIST *current, *head;
char *p, *s;
head = current = NULL;
printf ("Enter the name of the file: ");
scanf("%s",file_name);
fp = fopen(file_name, "r");
while(fgets(line, sizeof(line), fp))
{
p = s = line;
while(*p!=0)
{
if (*p==' ')
{
LIST *node = malloc(sizeof(LIST));
*p = 0;
node->string = strdup(s);
node->next =NULL;
if(head == NULL){
current = head = node;
} else {
current = current->next = node;
}
s = p+1;
}
p++;
}
}
fclose(fp);
//test print
for(current = head; current ; current=current->next){
printf(" %s", current->string);
}
return 0;
}
Several things:
You are using the same node for all words in a line, thereby creating a loop where node->next equals node. You should create a new node when you insert a new word, not when you read a new line.
You don't catch the last word of a line. You can make use of the fact that fgets retains the trailing new-line character and check for that in addition to checking for a space. You could also consider using isspace from <ctype.h>.
Alternatively, and maybe better, is to defer the check for the null terminator until after the loop. You must then add a new word when you read a space or a null character.
You insert empty words when the input file contains consecutive spaces or space characters. Your program should check whether p > s to add only valid words. (Or your program shoul only add valid words when the previously read character wasn't a space.)
You allocate memory for the nodes and the strings. You should free this memory before exiting the program.
Here's your main loop with the fixes described above:
while(fgets(line, sizeof(line), fp))
{
char *p = line;
char *s = line;
do {
if (*p== ' ' || *p == '\n' || *p == '\t' || *p == '\0') {
if (p > s) {
LIST *node = malloc(sizeof(LIST));
*p = 0;
node->string = strdup(s);
node->next = NULL;
if(head == NULL){
head = node;
} else {
current->next = node;
}
current = node;
}
s = p + 1;
}
p++;
}
while (*p != 0);
}

error with pointers and linked list node creation

I'm trying to read line input from a file, correctly parse the line, and add the three fields of information from the line onto a node in a linked list.
Here's my read from file function:
int readFile(char* file)
{
FILE *fp = fopen(file,"r");
char ch;
char line[50];
char string1[100];
char string2[100];
char string3[100];
char endLine[2];
int i = 0;
while(fscanf(fp, "%[^\t]\t%[^\t]\t%[^\n]", string1, string2, string3) == 3)
{
printf( "%s\t%s\t%s\n", string1, string2, string3);
addNode(string1, string2, string3, head, tail);
}
printNodes();
fclose(fp);
return 0;
}
And here is my addNode function:
// create stuff
Entry *entry = malloc(sizeof(Entry));
entry->name = string1;
entry->address = string2;
entry->number = string3;
Node* node = malloc(sizeof(Node));
node->entry = entry;
node->next = NULL;
// Empty list
if(head->next == NULL)
{
head->next = node;
}
// Else, add to the end of the list
else
{
Node* temp = head->next;
while(temp->next!= NULL)
{
temp = temp->next;
}
temp->next = node;
}
I get problems when I call printNodes, and only the last read node's information is printed X times, where X is the number of unique nodes I'm supposed to have. I think I'm having a problem where I'm overwriting an old node each time I create a new node, but I'm not entirely sure, as this is my first time with raw C code.
Thanks again!
EDIT:
here's the printNodes() function:
int printNodes(Node* head)
{
Node *temp = head->next;
while(temp->next != NULL)
{
printf("\n%s\t%s\t%s\n", temp->entry->name, temp->entry->address, temp->entry->number);
temp = temp->next;
}
return 0;
}
Your problem is here:
entry->name = string1;
entry->address = string2;
entry->number = string3;
You are providing the same memory location to every node. Those strings contain the last value you read in when you call printNodes().

Resources