I have a program which I am working on for an assignment. This has to be a linked list which reads character by character from a file and then creates words which then get written to the output file. I am able to compile the code with just warnings and no errors. When I try to run the program with an input.txt
and output.txt
the program runs and then terminal produces the first line which is file opened
followed by segmentation fault:11
I have no idea how to fix this or work around it to get it fixed.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct s_words {
char *str;
int count;
struct s_words* next;
}
words;
words *create_words(char *word) {
//allocate space for the structure
words *newWord = malloc(15);
if (NULL != newWord) {
//allocate space for storing the new word in "str"
//if str was array of fixed size, storage would be wasted
newWord->str = (char *)malloc(strlen(word));
strcpy(newWord->str, word); //copy word into newWord->str
newWord->str[strlen(word)] = '\0';
newWord->count = 1; //initialize count to 1;
newWord->next = NULL; //initialize next;
}
return newWord;
}
words *add_word(words *wordList, char *word) {
int found = 0;
words *temp = wordList;
// search if word exists in the list; if so, make found=1
while (temp != NULL) {
if (strcmp(temp, word)) { //use strcmp command
found = 1;
wordList->count++; //increment count;
return wordList;
} else {
temp = temp->next; //update temp
}
}
if (found == 0) { //new word
words *newWord = create_words(word);
if (NULL != newWord) {
wordList = newWord;
newWord->next = temp;
//?? Insert new word at the head of the list
}
return newWord;
}
}
int main(int argc, char *argv[]) {
words *mywords; //head of linked list containing words
mywords = NULL;
FILE *myFile;
myFile = fopen(argv[0], "r"); //first parameter is input file
if (myFile == 0) {
printf("file not opened\n");
return 1;
} else {
printf("file opened \n");
}
//start reading file character by character;
//when word has been detected; call the add_word function
int ch, word = 0, k = 0;
char thisword[100];
while ((ch = fgetc(myFile)) != EOF) {
if (ch == ' ') { //detect new word? Check if ch is a delimiter
if (word == 1) { //make sure previous character was not delimiter
printf("hi");
word = 0;
thisword[k] = '\0'; //make the kth character of thisword as \0
add_word(mywords, thisword); //now call add_word to add thisword into the list
mywords = NULL;
k = 0;
}
} else {
word = 1;
thisword[k] = ch; //make the kth character of thisword equal to ch
k++;
}
}
fclose(myFile);
words *currword;
printf("printing list\n");
words *temp;
//Traverse list and print each word and its count to outputfile
//output file is second parameter being passed
FILE *outputFile;
outputFile = fopen(argv[1], "w");
if (outputFile == 0) {
printf("file not opened\n");
return 1;
} else {
printf("file opened \n");
}
currword = mywords;
while (currword != NULL) {
fprintf(outputFile, "HI");
fprintf(outputFile, "%s %d" , currword, currword->count);
temp = currword;
currword = temp->next;
}
return 0;
}
Any help would be greatly appreciated
EDIT Here is the updated code: CODE
The new output is now this: here are the files test.txt results.txt file opened hihihihihihihihihihihihihihihiprinting list file not opened Abort trap: 6
The way you allocate the string in the word
structure is incorrect:
newWord->str = (char *)malloc(strlen(word));
strcpy(newWord->str, word); //copy word into newWord->str
newWord->str[strlen(word)]='\0';
You must allocate one more byte for the '\\0'
terminator:
newWord->str = malloc(strlen(word) + 1);
strcpy(newWord->str, word);
You can simplify this with a single call to strdup()
:
newWord->str = strdup(word);
Furthermore, you call strcmp(temp, word)
to compare the word with the string of the current entry, this is incorrect, and the compiler must have issued a warning for this error. You should instead write:
if (strcmp(temp->str, word)) {
In main()
, you call add_word()
incorrectly:
add_word(mywords, thisword); //now call add_word to add thisword into the list
mywords = NULL;
You should write:
mywords = add_word(mywords, thisword); //now call add_word to add thisword into the list
In the print loop, you pass currword
to printf
for %s
instead of currword->str
. You do not need an extra temp
variable either:
while (currword != NULL) {
fprintf(outputFile, "HI");
fprintf(outputFile, "%s %d" , currword->str, currword->count);
currword = currword->next;
}
EDIT : your function add_word
does not always return a value to the caller, and it does not correctly insert the newWord at the head of the list.
Here is a modified version:
words *add_word(words *wordList, char *word) {
// search if word exists in the list; if update count and return
for (words *temp = wordList; temp != NULL; temp = temp->next) {
if (strcmp(temp, word)) { //use strcmp command
wordList->count++; //increment count;
return wordList;
}
}
words *newWord = create_words(word);
if (newWord != NULL) {
// Insert new word at the head of the list
newWord->next = wordList;
wordList = newWord;
}
return newWord;
}
Finally, the reason you get a segfault might be that you do not check the array size when adding characters to it as you read from the input file. You may wonder why, since the input file probably has small words in it... but you are not reading the input file passed as the first command line argument, you are reading the executable file itself as you do this:
myFile = fopen(argv[0], "r"); //first parameter is input file
argv[0]
is the name of the executing program.
You should also change the other fopen
, otherwise you will overwrite your input.txt
file:
myFile = fopen(argv[1], "r"); //first parameter is input file
...
outputFile = fopen(argv[2], "w"); // second parameter is output file
Learning to debug yourself is important. There are some debugging tools available that you should take advantage of.
Compile your program with the -g flag, then run it with one of these to explore your problem. Also take your commenter's advice in fixing your warnings.
GDB https://www.gnu.org/software/gdb/
Valgrind http://valgrind.org/
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.