c program not outputting as expected - c

So I have this program that I put together from head first c. It is from Chapter 6 - the data structures chapter... My problem is that the output displays all previous listed entries as well as the last entered name to the standard input. So instead showing everything printed once the program prints everything almost twice. It is hard for me to describe. If you just copy and paste it into a text-editor on your machine and run the code you will see what I mean.
The book shows the program taking a file of island names using the < redirection tool. When I try this it prints the first name the second name and the first name. Then the next name and the second and first name...Then the next name and the third, second, and first name...etc depending how many names there are. This behavior also occurs when entering text in the terminal in standard input.
IF I change the code to say display(next) it works closer to what I would expect but it still prints out an extra blank line and there are probably memory leaks
This code is pretty much over my head can someone figure out why it is printing like this?
I would ask at the head first c discussion board but I wanted to ask stackoverflow first and get an immediate answer.
My code is below. If you copy and paste it into a text editor it should not look like a wall of text.
Happy coding.
#include <stdio.h> // basic input output
#include <stdlib.h> // for obtaining and releasing heap memory malloc and free...
#include <string.h> // for the stringdup method
typedef struct island {
char *name;
char *opens;
char *closes;
struct island *next;
} island;
void display(island *madonna);
island* create(char *name);
void release(island *start);
int main()
{
/* create islands */
island *start = NULL;
island *i = NULL;
island *next = NULL;
char name[80];
puts("enter island name...");
for(; fgets(name, 80, stdin) != NULL; i = next) {
next = create(name);
if(start == NULL)
start = next;
if (i != NULL)
i -> next = next;
display(start);
}
release(start);
}
// display method
void display(island *start)
{
island *i = start;
if (i == NULL)
puts("i equals NULL ");
for(;i != NULL; i = i ->next) {
printf("Name: %s open: %s-%s\n", i->name, i->opens, i->closes);
}
}
// create method
island* create(char *name)
{
island *i = malloc(sizeof(island));
i->name = strdup(name);
i->opens = "09:00";
i->closes = "17:00";
i->next = NULL;
return i;
}
// release method
void release(island *start)
{
island *i = start;
island *next = NULL;
for(; i != NULL; i = next) {
next = i-> next;
free(i->name); // must free this first because of strdup uses heap memory
free(i);
}
}

The code is working-as-designed (WAD). It is designed to print the complete list after each entry is read — that's what the display(start) does in the loop. You could help yourself by either echoing the input (printf("Read: %s", name); where there's no newline in the format because the name still includes the newline) or by tagging the display with printf("Printing list:\n"); before the call to display() (or both). If you get rid of the newline from the name, you'll need to adjust the 'echo' operation.
Learning how to create helpful diagnostic messages is a valuable technique; one of the key points is to ensure that the output lines end with a newline so there's a decent chance you'll see the printing as it occurs, rather than some indeterminate time later. Another key point is printing inputs so that you know what the code is working on rather than thinking you know what the code is working on. Printing the complete list on each iteration also helps ensure that the list is being constructed correctly. You can find examples on SO where the list was not constructed correctly (e.g. First address of struct). Had the complete list been printed on each iteration, the problem would have been more obvious.
The trouble, therefore, seems to be that your expectations do not match what the code is designed to do.

"My problem is that the output displays all previous listed entries as
well as the last entered name to the standard input."
for(;i != NULL; i = i ->next) {
printf("Name: %s open: %s-%s\n", i->name, i->opens, i->closes);
by the way you told us your problem but you did not tell us what your program should do

Related

Why is this conditional statement not working in C? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I'm trying to create a program in C that is able to create a list of characters (using a linked list) input by the user, and follow these commands: insert (adds a new character to the list), remove (clears the list and deletes all elements), print (prints the current contents of the list) and exit (closes the program). In order for it to work, the user must enter the command to execute first. For instance, if they type 'insert', they will be prompted to enter a character, if they type 'print', the program will print the list, and so on.
To approach this, I basically declared a char[] variable called command. Which represents the user's text input. After receiving the input, the program will compare the command string to the following strings: insert, remove, print, and exit, and if the strings match, then the program will execute the command that the string corresponds to (refer to the main below):
#include <stdio.h>
#include <stdlib.h>
struct linked_list
{
char data;
struct linked_list* next;
};
void insert(struct linked_list** head, char input);
void removed(struct linked_list** head);
void printed(struct linked_list* head);
void insert(struct linked_list** head, char input) {
int x;
x = ("%d",input);
if (x < 97 || x > 122) {
printf("The character you entered is invalid, please use the insert command and try again\n");
}
else {
struct linked_list* newNode = (struct linked_list*) malloc(sizeof(struct linked_list));
struct linked_list* lastNode = *head;
newNode->data = input;
newNode->next = NULL;
if (*head == NULL) {
*head = newNode;
return;
}
while (lastNode->next != NULL) {
lastNode = lastNode->next;
}
lastNode->next = newNode;
}
}
void removed(struct linked_list** head) {
struct linked_list* current = *head;
struct linked_list* next;
while(current != NULL) {
next = current->next;
free(current);
current = next;
}
*head = NULL;
}
void printed(struct linked_list* head) {
while (head != NULL) {
printf("%c ", head->data);
head = head->next;
}
printf("\n");
}
int main(int argc, char *argv[]) {
char command[] = "";
char inserts[] = "insert";
char removes[] = "remove";
char prints[] = "print";
char exits[] = "exit";
char input;
struct linked_list* head = NULL;
int done = 1; //Variable used to keep the loop going until the user types 'exit'
printf("Welcome, please use the commands 'insert', 'remove', 'print' or 'exit' to use the program\n");
while (done == 1) {
scanf("%s", &command);
int result1 = strcmp(command,inserts);
int result2 = strcmp(command,removes);
int result3 = strcmp(command,prints);
int result4 = strcmp(command,exits);
if (result1 == 0) {
printf("Enter a char: ");
scanf("%s", &input);
insert(&head, input);
result1 = 1;
}
else if (result2 == 0) {
removed(&head);
result2 = 1;
}
else if (result3 == 0) {
printed(head);
result3 = 1;
}
else if (result4 == 0) {
printf("The program will close now");
done = 0;
}
else {
printf("Unrecognized command, please try again\n");
}
}
return 0;
}
The solution I came up with works perfectly for the commands insert, remove, and print. However, for some reason I can't quite understand it doesn't work for exit, it will only work if and only if exit is the first command you enter (which doesn't make sense, since why would you exit a program without even using it). There are no typos or other apparent errors. You can compile the code and see for yourself. What might be causing this issue? Why does it work for three of the command words but not for one?
Thanks in advance
UPDATE: After messing around with the code a little bit, I realized none of the suggestions provided were the issue. I simply changed my loop statement to be : else if (strcmp(command,"exit")==0) and it worked perfectly
x = ("%d",input);
involves the comma operator. The left operand "%d" is evaluated (to itself, since a literal string, then discarded) and the right operand input is evaluated and result of that comma operator.
So x becomes input
You may want to spend a dozen of minutes reading about scanf or about getchar. It can fail. You should handle both failure and success (with some test).
My personal recommendation : enable all warnings and debug info in your compiler. With GCC, compile with gcc -Wall -Wextra -g. Improve your code to get no warnings. Then use your debugger (e.g. GDB) to understand the behavior of your program.
Of course you will spend some time reading the documentation of your compiler and of your debugger.
Don't forget to spend some time reading a good book on the C programming language. Refer also to this C reference. Study also for inspiration the source code of existing free software (like GNU bash) coded in C. You will learn a lot by reading existing reviewed C code.
You may also want to read something about parsing techniques. You could consider (if your teacher allows that) using parser generators like GNU bison (which generates C code).
Be aware that in 2021 UTF-8 is used everywhere.
Be scared and avoid undefined behavior.
In some cases, a program appears to work, but does not. Read some C standard (like n1570) explaining it. I guess your program has several buffer overflows.
You could also (if your teacher allows it) use tools like the Clang static analyzer, valgrind or the address sanitizer
The reason your commands aren't working as intended is that you declare zero-length string literal:
char command[] = "";
then read user input into it:
scanf("%s", &command);
The user input isn't going to fit into zero characters, so it's going to overwrite something else in memory, likely something important.
You need to declare an actual buffer that's large enough to hold the largest expected input, something like this:
char command[10];
You also need to clear or initialize that buffer, perhaps using memset(command, 0, sizeof(command));
Something similar happens with input. It's declared as char but then you read a string (%s) into it:
scanf("%s", &input);
Even if you only type one character, scanf still has to store a terminating NUL, so it will clobber something else in memory.
With all these buffer overwrites going on, what the program actually does, and why exit only works as the first command, is anybody's guess.

C code to create a linked list segmentation fault

Ok, so for my homework I have a function that creates a linked list from file input and it's like a calendar, so it reads in a date and title from a file. I'm using a head node that is globally declared to make it
easier to work with other functions.
This is my node struct:
typedef struct event_t{
char title[20];
event_date_t date;
struct event_t *next;
}event_t;
event_date_t is just a simple struct for the date
Here's the function:
void insert_events_linked_list(FILE *file, int n){
//printf("LL function started\n"); //self-explanatory test line
event_t last;
head.next = &last;
int i;
//This loop will create a ll of the specified length
for(i=0; i<n; i++){
event_t *last = malloc(sizeof(event_t));
int title_test = fscanf(file, "%20s%*c", last->title);
printf("%s\n", last->title); //test line to make sure names are grabbed properly
//This skips the rest of the event and prints error message if title is too long
if(title_test != 1){
fscanf(file, "%*s %*d %*d");
printf("Error: LL event %d title too long\n", i++);
continue;
}
else{
fscanf(file, "%d %d", &last->date.month, &last->date.day);
last = last->next;
}
}
printf("Loop exited");
}
The test line prints all the titles, but it shows a segmentation fault and aborts before printing "Loop exited"
This line of your code,
event_t *last = malloc(sizeof(event_t));
will declare a brand new last every time the for loops back. Declare it as static and adjust your code accordingly, and there should no longer be any problem.
Demo
for (i=0;i<5;i++){
int x=6;
x++;
printf("%d ",x);
}
Output
7 7 7 7 7
You should see the problem.
The test line prints all the titles, but it shows a segmentation fault and aborts before printing "Loop exited"
There are many bugs in your program. You should read this post. You should also learn to use a debugger.
I suspect that the reason Loop exited is not printed is that you didn't terminate it with the newline, and so printf is waiting for you to complete a line or call fflush. By default, printf output is line buffered. That is one of the reasons you should always use fprintf(stderr, ...) instead. stderr (by default) is unbuffered.
On to bugs.
You have two variables called last in the same scope. This is almost never a good idea.
This statement:
head.next = &last;
assigned an address of a local variable to a global head pointer. After the current function returns, that address will become invalid, and this is almost certainly not what you intended. In particular, head.next never points to any memory you malloc (and leak!) in the loop.
Your loop looks like this:
for (...) {
event_t *last = malloc(sizeof(event_t)); // last->next is uninitialized
...
last = last->next; // leak the memory allocated above
// by overwriting the pointer with garbage
}
That is also clearly not what you intended.
Here is one way to build a linked list incrementally:
event_t **link = &head.next;
for (...) {
event_t *ev = calloc(...); // Initializes ev->next to NULL.
// Fill the rest of ev ...
// Link it into the list:
*link = ev;
link = &ev->next;
}

My program can only use malloc() to allocate memory a few times

I wrote a binary search tree to store some sorted words. As is often the practice, I do this by allocating new block of memory for the binary tree every time a new word come in. But, strangely, I can only allocating new memory for the binary search tree twice, which means that at the first and second time everything was fine but the program crash at the third memory allocation.
Here is my code:
inputWord.c
/* I pass in the firstNode, and the word I wanna store, and its quantity as argument*/
int inputWord(BSTnode* Node,char* word,int num){
BSTnode* ptr=Node; //ptr was defined to track the location of the node.
while(1){
if(stricmp(word,ptr->data)>0){
/*If the current node already have a rightchild then ptr move to it, and do comparison again*/
if(ptr->rightchild!=NULL){
ptr=ptr->rightchild;
printf("Moving to another (right) node now!!\n");
continue;
}
/*If the current node have no rightchild, then make a new one for it and store the word and its quantity*/
else{
ptr->rightchild=malloc(sizeof(BSTnode));
if(!(ptr->rightchild))
return 1;
ptr=ptr->rightchild;
ptr->rightchild=NULL;
ptr->leftchild=NULL;
strcpy(ptr->data,word);
ptr->num=num;
break;
}
}
else if(stricmp(word,ptr->data)<0){
/*it's all the same as the rightchild part*/
if(ptr->leftchild!=NULL){
ptr=ptr->leftchild;
continue;
}
else{
ptr->leftchild=malloc(sizeof(BSTnode));
if(!(ptr->leftchild))
return 1;
ptr=ptr->leftchild;
ptr->leftchild=NULL;
ptr->rightchild=NULL;
strcpy(ptr->data,word);
ptr->num=num;
break;
}
}
/*If the word have already been stored in the tree, print out this message*/
else{
fprintf(stdout,"It is exactly the same word!!\n");
return 0;
}
}
return 0;
}
I have make some necessary comments above to help you understand my intention.Hopefully that would help.
As you can see, that function was pretty straight and simple. And it did work for the first two invokation.But it crash when invoked the third time!!(always the third time).
So I made some test. And now I am pretty sure that it crash at the line
ptr->leftchild=malloc(sizeof(BSTnode));
(make it clear that the data offirstNode is initialize with "" for comparison. And I pass in the word "The" first and "Project" second and "Gutenberg" third. And the structure of BSTnode is
typedef struct BSTnode{
char data[20];
struct BSTnode* leftchild;
struct BSTnode* rightchild;
int num;
}BSTnode;
)
How I make that test is listed as below. (It is the same code, only with some extra print statement for test)
int inputWord(BSTnode* Node,char* word,int num){
printf("Enter inputWord() successfully!!\n");
BSTnode* ptr=Node;
while(1){
if(stricmp(word,ptr->data)>0){
if(ptr->rightchild!=NULL){
ptr=ptr->rightchild;
printf("Moving to another (right) node now!!\n");
continue;
}
else{
printf("I need a new rightchild!!\n");
ptr->rightchild=malloc(sizeof(BSTnode));
printf("New rightchild created successfully!!\n");
if(!(ptr->rightchild))
return 1;
ptr=ptr->rightchild;
ptr->rightchild=NULL;
ptr->leftchild=NULL;
printf("......In line 27 now!!\n");
strcpy(ptr->data,word);
printf("Copied successfully!!!..In line 29 now!!\n");
ptr->num=num;
fprintf(stdout,"New data '%s' successfully inserted into a new (right) node at %p (value of pointer)\n",word,ptr);
break;
}
}
else if(stricmp(word,ptr->data)<0){
if(ptr->leftchild!=NULL){
ptr=ptr->leftchild;
printf("Moving to another (left) node now!!\n");
continue;
}
else{
printf("I need a new left child!!!\n");
ptr->leftchild=malloc(sizeof(BSTnode));
printf("New leftchild created successfully!!\n");
if(!(ptr->leftchild))
return 1;
ptr=ptr->leftchild;
ptr->leftchild=NULL;
ptr->rightchild=NULL;
printf("......In line 47 now!!\n");
strcpy(ptr->data,word);
printf("Copied successfully!!!..In line 51 now!!\n");
ptr->num=num;
fprintf(stdout,"New data '%s' successfully inserted into a new (left) node at %p (value of pointer)\n",word,ptr);
break;
}
}
else{
fprintf(stdout,"Nothing else to insert!!\n");
return 0;
}
}
return 0;
}
As you can see, with some print statements telling me where have I been, I can be sure where the program crash.
Any idea why it always crash at the third time?
#######################################################################3
main.c
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<stdbool.h>
#include "wordCount.h"
void prompt(BSTnode*,FILE*);
char arr[20]={0};
int main()
{
BSTnode* firstNode=malloc(sizeof(BSTnode));
firstNode->leftchild=NULL;
firstNode->rightchild=NULL;
strcpy(firstNode->data,"");
firstNode->num=0;
FILE* fs=fopen("testfile.txt","r");
if(!fs){
printf("Failed to open fiel!!\n");
return 2;
}
while(1){
if(ferror(fs))
perror("there is a error in fs in the beginning of while loop!\n");
prompt(firstNode,fs);
}
return 0;
}
void prompt(BSTnode* Node,FILE* fs){
int i=0;
printf("Please select\n1.find and input a word into the binary tree\n2.print only one data\n3.Exit\n");
if(scanf("%d",&i)!=1){
printf("scanf failed!!\nplease input a valid number!!\n");
//fflush(stdin);
return;
}
getchar();
switch(i){
case 1:
{
memset(arr,'\0',20); //since the "arr" is used to hold the newWord founded and returned, it should be clear first every time
char* newWord=findWord(fs);
int totalNumberOfTheWord=wordCount(fs,newWord);
inputWord(Node,newWord,totalNumberOfTheWord);
break;
}
case 2:
printOneNode(Node);
break;
case 3:
exit(0);
default:
printf("Please input a valid number!(1-3)");
}
}
Also, the wordCount.h:
#ifndef WORDCOUNT_H
#define WORDCOUNT_H
#include<stdlib.h>
#include<stdio.h>
typedef struct BSTnode{
char data[20];
struct BSTnode* leftchild; //if less than, put it on the left
struct BSTnode* rightchild; //if greater than, on the right
int num;
}BSTnode;
int inputWord(BSTnode*,char*,int);
char* findWord(FILE*);
int wordCount(FILE*,char*);
int printOneNode(BSTnode*);
#endif
The function prompt() is used to prompt the user to decide whether to continue word-searching.
#####################################################################3
full source code:
wordCount.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "wordCount.h"
int wordCount(FILE* fs,char* word)
{
int num=0;
rewind(fs);
size_t n1=sizeof(word);
size_t n2=strlen(word);
char* buff=malloc(n1) ;
if(buff==NULL)
return 1;
memset(buff,'\0',n1);
/* I count the word by moving byte by byte and do comparison*/
if (fs != NULL) {
if (n2 == fread(buff, 1,n2, fs)) {
do {
if (strnicmp(buff,word,n2) == 0)
num++;
memmove(buff, buff+1,n2-1);
} while (1 == fread(buff+n2-1, 1, 1, fs));
// I think I might optimize
// this using KMP algorithm
}
}
free(buff);
return num;
}
findWord.c
#include<string.h>
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include "wordCount.h"
extern char arr[20];
char* findWord(FILE* fs)
{
static long pos=0;
fseek(fs,pos,SEEK_SET);
if(ferror(fs)){
perror("fseek() failed!!!\n");
fprintf(stderr,"fseek() failed in file %s\n",__FILE__);
exit(EXIT_FAILURE);
}
char chr[1]={0};
bool flag1=false;
bool flag2=false;
while((1==fread(chr,1,1,fs))&&(!(flag1==false&&flag2==true))){
// This would make the findword() function
// find only a single word once
if(chr[0]!=32){
strncat(arr,chr,1);
flag2=true;
flag1=true;
}
else
flag1=false;
}
/*the key method that I use to find a new word is that I use two 'bool' flags: flag1 and flag2.
*Only when the "arr" is filled only with character, not a single space, will the flag1 be false and flag2 be true, thus breaking the while loop*/
pos=ftell(fs)-1;
//maybe everytime you use "fseek()", "ftell()", the
//file-position will move one byte ahead.
return arr;
}
printOneNode.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"wordCount.h"
int printOneNode(BSTnode* Node){
BSTnode* ptr=Node;
while(1){
printf("Select which side of node do you want to print now(l/r)?(q for quit) ");
char a;
getchar(); //this is used to consume the newline character left
//fflush(stdin);
if(scanf("%c",&a)!=1){
printf("scanf failed!!");
return 1;
}
switch(a){
case 'l':
{
if(ptr->leftchild!=NULL){
ptr=ptr->leftchild;
printf("\t%s\n",ptr->data);
}
else
printf("There is no more leftchild\n");
break;
}
case 'r':
{
if(ptr->rightchild!=NULL){
ptr=ptr->rightchild;
printf("\t%s\n",ptr->data);
}
else
printf("There is no more rightchild!\n");
break;
}
case 'q':
return 0;
default:
return 0;
}
}
}
The function findWord() is used to find a new word for insertion. For example, if there is string This is a lovely place... in the textfile.txt, then the findWord() would first find out a word This and then is secondly and then a thirdly, etc. (This is the reason why I define the pos as a static variable to keep track of the location.)
The function wordCount() is used to count out how many time those the word returned by findWord() appear in the testfile.txt.
The function printOneNode() is used to print out the data of one single node according to the user's willingness. I designed this function but haven't use it yet, which mean that in the prompt() function I always choose to "find and input a new word into the binary search tree"). So this may not the reason that cause my program to crash "occasionally".
As summary, my routine is:
prompt the user asking whether to find and insert a new word( always yes)
find a new word in the testfile.txt using findWord()
count the number using wordCount()
insert it into the binary search tree using inputWord()
Repeat that.
I cannot make this program smaller any more to make it more understandable, because it have to find a word and count it insert it. But you can ignore that printOneNode() function, to some extent.
As for the testfile.txt, I have posted the link below at the comment area. Thanks
edit: This is an amendment to my previous post (found below), detailing the more severe issues found in this code.
In wordCount there is a buffer overflow. Buffer overflows are UB.
You're allocating n1 bytes for buff to point at. By chance, do you happen to know how many bytes that is? Perhaps you should check, and then answer this to yourself: How many bytes can you store in that object?
You're then attempting to read n2 bytes into buff. Which is greater, n1 or n2? Have you looked at that? What happens if you try to fit 24 eggs into a carton that only holds 12?
I think the problem here is that you don't understand the sizeof operator; it isn't a function... Rather, it is an operator much like the &address-of and the -negation operator, except that sizeof operates on the type of (or denoted by) an expression; it evaluates to the size of objects of that type.
To clarify, in the following fragment of code, n1 is sizeof (char *), which is probably not what you intended.
int wordCount(FILE* fs,char* word)
{
int num=0;
rewind(fs);
size_t n1=sizeof(word);
size_t n2=strlen(word);
char* buff=malloc(n1);
inputWord seems to operate under the impression that word points to a string, however that value seems to come from findWord in your program, which doesn't necessary produce a string (because it uses strncat). More undefined behaviour! Is this surprising?
Previous answer:
Firstly, this code doesn't even compile. You're missing a semicolon immediately following inputWord(Node,newWord,totalNumberOfTheWord) within prompt. Perhaps you haven't noticed the errors, and you're running an out-of-date binary which we don't have the source code for?
Secondly, even if this code were to compile, there are a number of instances of undefined behaviour such as:
Null pointer dereferences occur when malloc returns NULL and you attempt to modify the object which NULL points to as a result. e.g. BSTnode* firstNode=malloc(sizeof(BSTnode)); followed immediately by firstNode->leftchild=NULL;. Perhaps you could declare firstNode like so: BSTnode firstNode = { 0 }; and create a pointer to it using &firstNode... After all, you really should choose the most appropriate storage duration rather than defaulting to malloc every time. On that note, I highly recommend separating your allocation logic from your data structure logic; if you need further elaboration, consider how scanf is designed.
fflush(stdin);. Whenever you use a function for the first time, you should always read and understand the manual very carefully... and that's not just to provide insight on how you should be designing your functions. If you had read and fully understood this fflush manual prior to using fflush, you would have never used this problematic code. Consider using something like scanf("%*[^\n]"); getchar(); in its place.
In a few places you're using the %p format directive, which expects a void * pointer as a corresponding argument. The corresponding argument you're providing, however, is of type struct BSTnode *. According to the fprintf manual, "If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined."
Even if you don't fix these undefined behaviours, this code may coincidentally work on your system when you provide dummy functions in place of findWord and wordCount. However, it's not required to work the same way on all systems, which means for you the crash might occur where for us it doesn't. Fix those problems.
Those problems indicate that your findWord and wordCount functions aren't necessarily trustworthy and foolproof, either; they might work for you in one setting whilst failing for you in another, or worse yet, perhaps they're stale too! You should have verified that the problem is where you think it is by providing dummy functions in their places. That is, after all, part of the process of creating an MCVE so that your question doesn't get closed.
No, I won't be interested in starting a bounty on this question because it's of extremely poor quality; as I previously mentioned, this question relies upon syntactically erroneous code compiling correctly, so we can't reproduce the result you see. Even if we fix the syntax errors, we'd have to fill in the blanks (that's your work) which introduces an aspect of uncertainty into any possible answers. About the only thing I am interested in starting for this question is the process of having it closed.

Trie Implementation in C: Segmentation Fault

I'm currently doing CS50 from Harvard and the goal is it to load a dictionary into any data structure in the fastest way possible. For this problem set I'm using a Trie.
The logic behind my code is as follows:
Read one character at a time.
Check in the childnode of the trie if the character already exists, if it equals NULL, we allocate some space to it.
The curser is set to the childnode we just allocated space to.
If we reach the end of a word ("\n") we set the boolean value to true and completely reset the curser to its initial value (which we have stored previously in the curser->root).
I have tried several implementations, some of them had a few logical errors which I was not happy with and some gave me segmentation faults when I had a big dictionary.
Below is the code of my latest implementation, basically what happens is that it's fine with loading the first word into the trie structure, but it fails at the second. The problem then lays into me setting the new node value to the childenode (to which we allocated some free space). The logic behind this is it to obviously connect the tree and move on to the next node. This is the code which I think is wrong:
curser = curser->children[tolower(ch) - 'a'];
But the thing is, it worked in some of my other implementations, only with this one it stopped working all of a sudden and gave me a segmentation fault after the first word. As I said, I'm a beginner in coding so please enlighten me and criticize my implementation! Thanks a bunch.
#include <stdbool.h>
#include <stdio.h>
#include "dictionary.h"
#include <ctype.h>
#include <stdlib.h>
typedef struct node
{
bool end;
struct node* children[27];
struct node* root;
struct node* next;
} node;
//global variable used to check the number of words in the trie
int totalwords = 0;
//root node
node* curser;
int ch;
int main(void)
{
FILE* dict = fopen("text.txt", "r");
if (dict == NULL)
{
printf("Could not open dictionary\n");
return 1;
}
curser = (struct node*) malloc(sizeof(node));
curser->root = curser;
for (ch = fgetc(dict); ch != EOF; ch = fgetc(dict))
{
if (ch == '\0')
{
curser->end = true;
curser = curser->root;
totalwords++;
printf("%i\n", totalwords);
}
else
{
if (isalpha(ch))
{
if (curser->children[tolower(ch) - 'a'] == NULL)
{
curser->children[tolower(ch) - 'a'] = (struct node*)malloc(sizeof(node));
}
curser = curser->children[tolower(ch) - 'a'];
}
else if (ch == '\'')
{
if (curser->children[26] == NULL)
{
curser->children[26] = (struct node*)malloc(sizeof(node));
}
curser = curser->children[26];
}
}
}
fclose(dict);
return false;
}
Edit:
Another question I have is why my in my current code it is not able to detect the Null Terminator \0 but it can detect a new line \n? I need to be able to detect the null terminator in order to get the correct amount of words. Any suggestion as to what is wrong?
After curser->root=curser; You should do the following:
curser->end=false;
curser->next=NULL;
for(i=0;i<27;i++)
curser->children[i]=NULL;
When you initialize memory for curser it is not guaranteed that it's members will be automatically allocated to NULL and false.
Do this everywhere for a node you are allocating memory dynamically.
You also need to set child->root=curser->root for every children you are allocating memory dynamically
It looks as if this relates to Pset5 of CS50, and you are trying to implement the loading of a dictionary. As it happens, you are using the fgetc function to read a single letter from a text file, and not from memory.
When you are reading from memory, there will be a '\0' NULL terminator for the word. However, with fgetc you are using stdio to read from the file and the '\0' terminator does not exist in that file. Since the words in the CS50 dictionary are stored one word per line, and all lines end with a '\n' ("new line"), it can be found that way.

C language, where is the segmentation fault?

here is the function where im getting the segmentation fault
void searchcity()
{
struct city *ptr=citylist;
printf("Which city would you like me to search?: ");
scanf("%s",searchedcity);
// printf("%s",searchedcity);
while(ptr)
{
if(!strcmp(searchedcity,ptr->name))
printf("name= %s, statecode = %s,population = %s,region = %s,zipcode = %s\n",ptr->name,ptr->statecode,ptr->population,ptr->region,ptr->zipcode);
else
printf("sorry, couldnt find that city");
ptr=ptr->next;
}
}
not sure what can be causing this to happen.
Based on that code (a), here's what you need to check at a bare minimum:
That searchedcity has enough space for the input (b).
That all the strings held in the citylist linked list are properly constructed (null-terminated).
That all those fields in the structure are actually character arrays (or equivalent pointers) rather than integers (population, for example).
That the list itself is properly built (no dangling or invalid pointers).
You do have one other problem, though nothing to do with the segfault.
Your code will print "sorry, couldnt find that city" for every node in the list that doesn't match your city so, if you have New York, Moscow and London, and you look for London, you'll get that message printed twice before it finds it.
A better solution (one of many variants) would be something like:
struct city *ptr = citylist;
while (ptr != NULL)
if (strcmp (searchedcity, ptr->name) == 0)
break;
if (ptr == NULL)
printf ("Sorry, couldnt find that city.\n");
else
printf ("%s, state = %s, pop = %s,r egion = %s, zip = %s\n",
ptr->name, ptr->statecode, ptr->population, ptr->region, ptr->zipcode);
That way, the loop is responsible for either finding the correct pointer or setting it to NULL. After the loop is the correct time to decide what should be printed.
(a) That code itself seems okay other than the dangerous scanf but it does depend on quite a lot of other stuff not shown.
(b) In fact, scanf with an unbounded %s is a serious hole in your code that can easily lead to buffer overflow. See here for details and solution. In any case, scanf("%s") is not a good way to scan strings with spaces in them since something like Los Angeles would end up as Los :-)
Code below works, I did minor changes you can walk through it. Few small mistakes you had was your ptr->next was never executed due to missing bracket. The rest I wrote in the code.
Thanks hope we helped.
#include <stdio.h>
struct city {
char name[100];
char statecode[100];
char population[100];
char region[100];
char zipcode[100];
struct city* next;
};
int main() // this is your searchcity function
{
char searchedcity[100]; // make sure you initialize this. YOu haven't done it in the code you gave us.
// Assume citylist is in your main function or initialized as a global var
// I initialized it here for simplicity
struct city* citylist = (struct city*) malloc(sizeof( struct city));
strcpy(citylist->statecode,"statecode");
strcpy(citylist->population,"population");
strcpy(citylist->region,"region");
strcpy(citylist->zipcode,"zipcode");
citylist->next = NULL;
//end of citylist
struct city *ptr = citylist;
printf("Which city would you like me to search?: ");
scanf("%s",searchedcity);
// printf("%s",searchedcity);
while(ptr) {
printf("while \n");
if(!strcmp(searchedcity,ptr->name)){
printf("name= %s, statecode = %s,population = %s,region = %s,zipcode = %s\n",ptr->name,ptr->statecode,ptr->population,ptr->region,ptr->zipcode);
}else{
printf("sorry, couldnt find that city");
ptr=ptr->next;
}
}
return 0;
}

Resources