program crashes while iterating through a linked list - c

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

Related

how would I be able to replace string values stored in one linked list with string values from another linked list?

I was trying to update the values of the strings stored in a linked list that had English words with Spanish words. I thought I could change the list of English words and later display the updated translated list.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct node {
char *engword;
struct node *next;
};
typedef struct node eng;
struct new {
char *spanword;
struct new *next;
};
typedef struct new span;
eng *addEng(const char *word, eng *head)
{
eng *temp1;
temp1 = malloc(sizeof(*temp1));
// add char type word to linked list
temp1->engword = strdup(word);
temp1->next = head;
head = temp1;
return head;
}
span *addSpain(const char *word, span *head)
{
span *temp1;
temp1 = malloc(sizeof(*temp1));
// add char type word to linked list
temp1->spanword = strdup(word);
temp1->next = head;
head = temp1;
return head;
}
void eng_display(eng *head){
eng *current;
current = head;
while(current != NULL){
printf("word linked: %s\n", current->engword);
current = current->next;
}
}
void spain_display(span *head){
span *current;
current = head;
while(current != NULL){
printf("spanish: %s\n", current->spanword);
current = current->next;
}
}
void replace(eng *head, span *head1){
eng *current;
span *temp;
current = head;
temp = head1;
while(current != NULL){
current = malloc(sizeof(*current));
current->engword = (char*)calloc((strlen(temp->spanword)+1), sizeof(char));
current->engword = temp->spanword;
printf("translated: %s\n", current->engword);
current = current->next;
}
free(current);
}
int main(){
size_t size = 0;
char *line = 0;
eng *head;
head = NULL;
FILE *fO;
fO = fopen("english.txt", "r");
while(getline(&line, &size, fO) != -1){
char *name = strtok(line, " ");
//printf("the word '%s'\n", name);
while(name != NULL){
head = addEng(name, head);
name = strtok(NULL, " ");
}
}
eng_display(head);
char *nline = 0;
span *head1;
head1 = NULL;
FILE *Madrid;
Madrid = fopen("span.txt", "r");
while(getline(&nline, &size, Madrid) != -1){
char *w = strtok(nline, " ");
//printf("the word '%s'\n", w);
while(w != NULL){
head1 = addSpain(w, head1);
w = strtok(NULL, " ");
}
}
spain_display(head1);
replace(head, head1);
}
This program ran using two files.
english.txt
you
are
cool
and span.txt
tu
eres
guay
When I ran it, it just printed out this:(just from the replace())
translated:guay
translated:guay
translated:guay
I am not sure what I am missing. Does anyone have any suggestions for how to change the English linked list?
Your main problem is that you are accessing unassigned memory in current - there are others (read the comments in the code below and see the next section for a correct code)
void replace(eng *head, span *head1){
eng *current;
span *temp;
/* 1: You assign head to current but you run over it in 2 */
current = head;
temp = head1;
while(current != NULL){
/* 2 Here you just create a new structure */
current = malloc(sizeof(*current));
/* 2.5 Here you allocate the string but in the next line you just overwrite the pointer */
current->engword = (char*)calloc((strlen(temp->spanword)+1), sizeof(char));
current->engword = temp->spanword; /* This line leaks memory */
printf("translated: %s\n", current->engword);
/* 3 And your problem is here - current->next is garbage because the old value was overwritten in 2 */
current = current->next; /* This line leaks memory */
}
free(current); /* Here you probably free garbage */
}
You probably wanted something like this
void replace(eng *head, span *head1){
eng *current;
span *temp, *temp1;
current = head;
temp = head1;
while(current != NULL){
/* Free the old value */
if (current->engword != NULL)
free(current->engword);
current->engword = strdup(temp->spanword);
printf("translated: %s\n", current->engword);
current = current->next;
/* We will clean the span list once everything is replaced */
temp1 = temp; /* Store temp so we can free the memory once we advance */
temp = temp->next;
if (temp1->spanword != NULL)
free(temp1->spanword);
free(temp1);
}
}

head updates on each insertion while converting text file to singly linked list in C

As the question specifies I want to convert a text file into linked list. When I read the string elements from an array it works fine but while doing the same from file creates the issue of updated head. The following is what I've tried...
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
//creating structure
typedef struct node {
char *word;
struct node *next;
}node;
//function for creating a node
node *createnode(char *wrd){
node *temp1;
temp1 = (node*)malloc(sizeof(node));
temp1->word = wrd;
temp1->next = NULL;
return temp1;
}
//function for creating and adding new node to linked list
node *creat(node *head, char *wrd){
node *temp, *p;
temp = createnode(wrd);
if(head == NULL){
head = temp;
}else{
p = head;
while(p->next != NULL){
p = p->next;
}
p->next = temp;
printf("p->word from creat method: %s ADDR: %p HEAD: %s \n", p->word,
p->next,head->word);
}
return head;
}
//traverse the list and display each node
node *show(node *head){
node *p;
p=head;
while(p != NULL){
printf("from show method: %s", p->word);
p = p->next;
printf("\n");
}
printf("\n");
}
int main()
{
node *head = NULL;
/* Character array to read the content of file */
char sWord[20];
/* Pointer to the file */
FILE *fp;
/* Opening a file in r mode*/
fp= fopen ("hw10data.txt", "r");
while( fscanf(fp, "%s", sWord) != EOF )
{
head = creat(head, sWord); //create list
}
fclose(fp);
show(head);
return 0;
}
I don't know why the head is updating on each insert. Any help will be appreciated.
It's not the head of the list which is changing; it's just that you read in the values into a local array sWord, which you then pass to the creat and createnode-functions. Therein, you simply assign a pointer, but you do not copy the content of sWord; Hence, all node contents will point to the same memory address, i.e. to sWord; and when you read in new values, all node contents will point to that new value. So it just seems as if the "head" has changed. Actually all nodes will show the same content...
To overcome this, write the following in createnode:
// temp1->word = wrd;
temp1->word = strdup(wrd);
Or, if strdup is not available:
temp1->word = malloc(strlen(wrd)+1);
strcpy(temp1->word,wrd);

Having trouble inserting a node before another node in a linked list?

I'm attempting to alphabetize a linked list as I insert into it (from a file) - I currently have three checks (I'll be using this to explain my logic - if someone has a better idea, please let me know, I've been pulling my hair out over this for the entire day):
If the word grabbed from the file matches the word in the current node, simply increment the frequency counter in the node (don't bother creating a new node). If the word comes before the current node, point previous->next to the newly created node, and point new_node->next to current node. If the word comes after the current node, point the new node to current_node->next, then set current_node->next to the new_node.
My problem is that when I try to run this program and use a file where the second word in the file comes BEFORE the first word, and try to print the linked list, I get locked in an infinite loop - I've narrowed down the issue to the fact that somewhere, a node pointer isn't getting updated, but I don't know where, and I'm at my wit's end.
I'll attach the two files that I have, if anyone could help me, I'd really appreciate it. (There are a few debugging lines in there that aren't really necessary, I was using them to attempt to figure out where the problem was.)
most_freq.h
#ifndef MOST_FREQ_H_
#define MOST_FREQ_H_
#include <stdio.h>
//used to hold each "word" in the list
typedef struct word_node
{
char *word;
unsigned int freq; //frequency of word
struct word_node *next;
} word_node;
struct node *readStringList(FILE *infile);
int readLine(FILE *infile, char * line_buffer);
struct node *getMostFrequent(struct word_node *head, unsigned int num_to_select);
void printStringList(struct word_node *head);
void freeStringList(struct word_node *head);
#endif
most_freq.c
#include "most_freq.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* TODO
0. Check if item is in list (make all strings lowercase)
1a. if not, insert into list
1b. if it is, increment counter for struct */
struct word_node *head = NULL; //unchanging head node
char* str_buffer = NULL;
struct node *readStringList(FILE *infile) {
char * line = NULL;
size_t len = 0;
ssize_t read;
char* tmp_buffer = (char*) malloc(sizeof(char) * 255);
while(readLine(infile, tmp_buffer) == 1) {
if(head == NULL) { //if the linked list is empty
//allocate space for node
head = (word_node*) malloc (sizeof(word_node));
//set as head node
str_buffer[ strlen(str_buffer) - 1 ] = '\0';
head->word = str_buffer; //set string of node to str_buffer
head->freq = 1; //set frequency to 0
head->next = NULL; //since we're at the first item of the list there is no next
}
else { //else, there is already a node in the list
printf("Linked list has a node.\n\n");
struct word_node *curr = head; //set curr to head (for traversal)
struct word_node *prev = head; //to keep track of the last node
while(curr != NULL) { //while current is not null, continue traversal
/* If string buffer matches current node's word, then simply update the frequency count */
if(strcmp(str_buffer,curr->word) == 0) { //if word matches the word in the list
curr->freq++; //update the current node's frequency
break;
}
/* If string buffer comes after current word (in alphabet) then point temp node->next to current->next, and point current node->next to temp */
else if(strcmp(str_buffer,curr->word) > 1) {
printf("Word comes after current node.\n");
word_node* temp = (word_node*) malloc (sizeof(word_node)); //allocate node for current str_buffer
temp->word = str_buffer;
temp->next = curr->next; //set temp node->next to current node's next
curr->next = temp; //set current->next to point to newly inserted node
}
else { //otherwise, str_buffer must come before current node
printf("Word comes before current node.\n");
word_node* temp = (word_node*) malloc (sizeof(word_node)); //allocate node for current str_buffer
temp->word = str_buffer;
printf("Previous Node: %s\n", prev->word);
printf("Current Node: %s\n", curr->word);
prev->next = temp;
temp->next = curr;
}
prev = curr;
curr = curr->next; //move current node up by one
}
}
}
printStringList(head);
exit(EXIT_SUCCESS);
}
int readLine(FILE *infile, char * line_buffer) {
char * line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, infile)) != -1) {
line_buffer = line;
str_buffer = (char*) malloc (sizeof(line));
strncpy(str_buffer, line_buffer, strlen(line));
if(str_buffer[0] != '\0') {
return 1;
}
else
return -1;
}
}
void printStringList(struct word_node *top) {
struct word_node *curr = top; //set curr to head (for traversal)
printf("List of Strings (and Frequencies)\n\n");
int count = 0;
while(curr != NULL) {
printf("%s (Frequency: %d)\n", curr->word, curr->freq);
curr = curr->next;
count++;
}
return;
}
int main(int argc, char *argv[])
{
FILE *file = fopen( argv[1], "r" );
/* fopen returns 0, the NULL pointer, on failure */
if ( file == 0 )
{
printf( "Could not open file.\n" );
}
else
{
readStringList(file);
}
}
test text file (passed in as a parameter when running from terminal)
foofoo
dog
else { //otherwise, str_buffer must come before current node
....
prev->next = temp;
temp->next = curr;
If the list has only one node then curr and prev point to the same node and you are introducing a loop here.Initially you are setting both prev and curr as head.You should set prev as NULL initially and then handle the case if the new node is going to become the first node(when prev will be NULL)
str_buffer[ strlen(str_buffer) - 1 ] = '\0';
head->word = str_buffer; //set string of node to str_buffer
Also you are allocating memory for temp_buffer and using str_buffer which is just a pointer.You might want to use temp_buffer here.
In this part:
else { //otherwise, str_buffer must come before current node
printf("Word comes before current node.\n");
word_node* temp = (word_node*) malloc (sizeof(word_node)); //allocate node for current str_buffer
temp->word = str_buffer;
printf("Previous Node: %s\n", prev->word);
printf("Current Node: %s\n", curr->word);
prev->next = temp;
temp->next = curr;
}
you need to add a check for curr == head (or curr == prev). That case will require special treatment as you need to update head.
Otherwise you get an endless loop. In that case your current code actually do:
head->next = newnode;
newnode->next = head;
which makes the list circular (and an endless loop while printing).
You need something like:
if (curr == head)
{
temp->next = head;
head = temp;
....
}
else
{
....
}
Apart from the fact that you need to handle adding to the front as a special case to update your head pointer, I find the memory allocation in your readLine routine confusing:
char* str_buffer = NULL; // 1
struct node *readStringList(FILE *infile) {
[...]
char* tmp_buffer = (char*) malloc(sizeof(char) * 255); // 2
while(readLine(infile, tmp_buffer) == 1) { // 3
int readLine(FILE *infile, char * line_buffer) {
char * line = NULL;
while ((read = getline(&line, &len, infile)) != -1) { // 4
line_buffer = line; // 5
str_buffer = (char*) malloc (sizeof(line)); // 6
strncpy(str_buffer, line_buffer, strlen(line)); // 7
You're doing three (3) memory allocations for each line read here, there's one done by getline at [4], since you passed in a NULL, another in readLine at [6], and a third one in readStringList at [2]. The tmp_buffer ([2]) isn't used anywhere in readStringList and neither does readLine use the respective argument (the pointer is just overwritten at [5]).
Also, there's no need to use a global variable here [1], your functions should use their arguments to pass the data (and have those arguments already).
Since you use getline, you can just return the buffer allocated by it to the outer function, and use it there directly.
Something like this:
int readLine(FILE *infile, char **line_buffer) {
char * line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, infile)) != -1) {
*line_buffer = line;
if(read == 0 || read == -1)
return -1;
return 1;
}
}
struct node *readStringList(FILE *infile) {
char* str_buffer;
while(readLine(infile, &str_buffer) == 1) {
// do something with str_buffer
// though remember to save it somewhere, you need to free() it later
}
[...]

Append a string from fscanf to linked list in 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.

Can't create singly linked list

I have an issue creating linked list: I don't know where I do an error in code, could you help me? Here's the code:
#include <stdio.h>
#include <stdlib.h>
#define LENGTH 255
struct node {
int info;
struct node *next;
} *head = NULL;
int create(FILE **data){
char read[LENGTH];
printf("Write data file name: ");
scanf("%s", read);
*data = fopen (read, "r");
if (data == NULL) {
printf("Error reading given file.");
}
return 0;
}
int put_Symbols_into_list(FILE *data) {
struct node *new_node, *current;
char c;
printf("Data given: ");
while (!feof(data)){
new_node = (struct node*)malloc(sizeof (struct node));
c = fscanf(data, "%s", &new_node -> info);
printf("%s ", &new_node -> info);
if (head == NULL){
head = new_node;
current = new_node;
} else {
current -> next = new_node;
current = new_node;
}
}
}
int main() {
FILE *data;
struct node *n;
create(&data);
put_Symbols_into_list(data);
//display_List(n);
return 0;
}
Steps that I do: Read data file for string and put it new node; if HEAD node doesn't have any data in it, put the read data in it; else put it in new node. Cycle this until there is not data left in data file. You can create new data file and put data in there, like 1 0 1 1 2 3 4 5 6.
You are not putting current->next to NULL after adding a new node. That will make a problem when you try to go through a list, since you won't know where it ends. I hope that's the problem that you're facing with.
Also you're having redundant code, since current will always point to new_node after adding it. So you don't have to put it both in if and else block. Just an advice.
You forgot to make the last node point to NULL. This will be extremely important when you try to traverse your list and display it.
int put_Symbols_into_list(FILE *data)
{
struct node *new_node, *current;
char c;
printf("Data given: ");
while (!feof(data)){
new_node = (struct node*)malloc(sizeof (struct node));
c = fscanf(data, "%d", &new_node -> info);
printf("%d ", new_node->info);
if (head == NULL){
head = new_node;
current = new_node;
} else {
current->next = new_node;
current = new_node;
new_node->next = NULL; // << added
}
}
return 0;
}
In addition to the answers above, you are declaring current as a local variable in put_Symbols_into_list, and not initializing current. Each time you exit put_Symbols_into_list, the value of current may be lost. You either need to declare current as static struct node *current, pass it as a parameter, or declare it globally. I would favor the static approach in this setting.

Resources