i have structure Trip and i make linked list for this Trip, when i add trip to the list i dont have problem, but when add the second trip it overrides the first one.
The input I do is adding two trips with id 1 and 2 and when i iterate them i see there is two times id 2
#include <stdio.h>
#include <stdlib.h>
char date[10];
char date2[10];
char newdate[10];
char newdate2[10];
int i,t,i2,t2;
int ret;
int option;
typedef struct trip
{
int code;
char startdate[10];
int duration;
double price;
} Trip;
typedef struct list
{
Trip* Trip;
struct list* next;
} List;
List* create_List(Trip* trip)
{
List* newList = malloc(sizeof(List));
if (NULL != newList)
{
newList->Trip = trip;
newList->next = NULL;
}
return newList;
}
void delete_List(List* oldList)
{
if (NULL != oldList->next)
{
delete_List(oldList->next);
}
free(oldList);
}
List* add_List(List* wordList, Trip* trip)
{
List* newList = create_List(trip);
if (NULL != newList)
{
newList->next = wordList;
}
return newList;
}
void createTripData(Trip *trip);
int main(int argc, char *argv[])
{
List* trips;
int first = 1;
while(1)
{
printf("Select an option: \n 1-add new trip \n 2-Iterate\n");
scanf("%d", &option);
if (option == 1)
{
Trip newTrip;
createTripData(&newTrip);
if(first == 1)
{
first = -1;
trips = create_List(&newTrip);
}
else
{
trips = add_List(trips, &newTrip);
}
}
else if (option == 2)
{
system("#cls||clear");
List* iter;
for (iter = trips; NULL != iter; iter = iter->next)
{
printf("Id %d \n", iter->Trip->code);
}
}
}
return (0);
}
void createTripData(Trip *trip)
{
fflush(stdin);
printf("Enter id: ");
scanf("%d", &trip->code);//
}
A problem with your code is the newTrip variable. You can not add it to the list the way you are trying. It is a local variable in a part of main. Therefore it goes out of scope (i.e. becomes invalid) when you exit that part of main. I'll suggest that you rewrite createTripData like:
Trip *trip createTripData()
{
Trip* t = malloc(sizeof(*t));
if (!t)
{
// Ups... add error handling
exit(1);
}
printf("Enter id: ");
if (scanf("%d", &t->code) != 1)
{
// Ups... add error handling
exit(1);
}
return t;
}
and call it like:
if (option == 1)
{
Trip* newTrip = createTripData();
if(first == 1)
{
first = -1;
trips = create_List(newTrip);
}
else
{
trips = add_List(trips, newTrip);
}
}
Besides that you can simplify your code by handling the empty list case inside add_List.
List* trips = NULL; // Notice this initialization
while(1)
{
printf("Select an option: \n 1-add new trip \n 2-Iterate\n");
scanf("%d", &option);
if (option == 1)
{
Trip* newTrip = createTripData();
trips = add_List(trips, &newTrip);
}
the function: createTripData() calls scanf() but fails to check the returned value (not the parameter values) so if the user enters just a return key or enters anything but digit(s) then the code accepts the nothing input, This is an error
The function: main() has two parameters, but neither is being used. the function main() has two valid signatures:
int main( int argc, char *argv[] )
int main( void )
since the parameters are not used, the code should use the signature:
int main( void )
The function: main() is getting the menu selection from the user via:
scanf( "%d", &option );
but not checking the returned value (not the parameter values) so when the user only enters a 'return' or any character other than a digit, the prior value in the field option is used. if the user entered any digit besides 1 or 2 then the code 'screams' through the while() loop, doing either nothing or per what ever value was previously in the variable option.
I.E. ALWAYS check the returned value from calls to any of the scanf() family of functions and handle errors.
in the function: main(), a assumption is made that the user entered either 1 or 2. That is NOT a valid assumption. (never trust the user to do the right thing) Strongly suggest the if( option == 1 and else if(option == 2) be replaced with a switch() statement that includes adefault` case for when the user enters any number other than 1 or 2 I.E.
switch( option )
{
case 1:
...
break;
case 2:
...
break;
default:
printf( "INVALID option: %d, valid options are 1, 2\n", option );
break;
}
this field, in the definition of struct list
Trip* Trip;
It is very poor programming practice to name a variable the same as the type of the variable. A possible fix:
Trip *myTrip;
Then update the other references in the code to use myTrip
It is very poor programming practice to name a variable the same as the type of the variable, with the only difference being the capitalization. Such practice leads to confusion in the human reading the code.
In function: create_list() what happens when the call to malloc() fails? A NULL pointer is returned to function: add_List() which returns that NULL pointer to function: main(), which (successful or not) overlays the value in the variable trips.
the statement:
fflush(stdin);
while being allowed in visual studio, is NOT portable. suggest using something like:
int ch;
while( (ch = getchar()) != EOF && '\n' != ch );
regarding the calls to scanf(), here is one way to handle the error events:
if( 1 != scanf( "%d", &trip->code ) )
{ // then error occurred as 'scanf' returns number of successful input/conversions
perror( "scanf for code failed" );
exit( EXIT_FAILURE );
}
// implied else, scanf successful
The above suggested method immediately exits the program (leaving the OS to cleanup all the allocated memory, which would be poor programming practice) You might want to, instead code a loop that informs the user of the problem, empties stdin and allowing the user to try again.
this statement:
printf("Select an option: \n 1-add new trip \n 2-Iterate\n");
will become very 'difficult' to read and understand if there are several choices in the menu. suggest making use of the C feature of bring all the consecutive strings together and writing the statement like this:
printf("Select an option: \n"
" 1-add new trip \n"
" 2-Iterate\n");
Note: the suggested formatting also pays attention to the right edge of the printed page, making for a much nicer layout.
the function: delete_List() is never called, so the 'menu' in main() is missing an option.
BTW: the function: delete_List() uses recursion, which is OK if the linked list is rather short. However, if there are a lot of entries in the linked list then (especially on windows) there is a high probability of overflowing the stack. I suggest using a loop to walk the linked list, passing each entry to free() after saving the ->next pointer Then it does not matter how many entries are in the linked list
for ease of readability and understanding:
1. separate code blocks (for, if, else, while, do... while, switch, case, default) via a single blank line
2. separate functions by 2 or 3 blank lines (be consistent)
3. follow the axiom: only one statement per line and (at most) one variable declaration per statement.
Now, why are you seeing the problem you mention in the question:
the second pointer passed to add_List() is to a uninitialized local pointer on the stack in main(), not to the actual pointer to the linked list that was declared by: List* trips;
Suggest using unique names so YOU can keep track of what is pointing to what.
Also,in function: create_List(), the new node is being set to point to that uninitialized pointer in main(), rather than to the desired entry in trips in main.
Note, in function: main(), in modern C compilers, if the returned value is always 0, then the return statement is not needed.
Note: the statement return is not a function, so there is no need (unless wrapping multiple expression into a single result value) to have parens around the value passed back by return.
for simplification, suggest elimination of the variable first, changing List* trips; to List *trips = NULL; and replacing: if(first == 1) with: if( !trips )
I.E. always try to write the simplest code that still performs the functionality.
Related
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.
I currently have a typedef pointer function that doesn't point to anything which result to a Segmentation fault (core dumped). I am trying to think of a solution to avoid it but can't think of any so far.
My code:
typedef void (*MenuFunction)(System*);
int main(int argc, char ** argv)
{
...
/* While loop for my menu */
while(1)
{
printf("Main Menu\n");
printf("%s\n", menu[0].text);
printf("%s\n", menu[1].text);
printf("%s\n", menu[2].text);
printf("Select your option (1-3): ");
(*getMenuChoice(menu))(&system);
}
}
/* Function that points to the menu function */
MenuFunction getMenuChoice(MenuItem * menu)
{
MenuFunction function = NULL;
char select[50];
fgets(select, 50, stdin);
if(select[strlen(select)-1] == '\n')
{
switch(select[0])
{
case '1':
function = menu[0].function;
break;
case '2':
function = menu[1].function;
break;
case '3':
function = menu[2].function;
exit(0);
break;
default:
printf("Invalid option\n");
}
}
else
{
readRestOfLine();
printf("Error: buffer overflow. Please try again, entering less data");
}
return function;
}
EDIT:
Right now, the solution I came up with was to create a function with nothing in it so I can have the function to point to something. I don't think this is ideal but it will do in the short run.
void skip()
{ }
A great strategy for managing all pointers (data functions as well as function pointers) is: have them always point to something valid, or be null. That is:
When you declare a pointer variable, always initialize it, either to point to something valid, or to NULL.
Before using a pointer, make sure it's not NULL.
When you do anything that would invalidate a pointer, set it back to NULL.
In your case, you're following rule 1 already, when you initialize
MenuFunction function = NULL;
So your getMenuChoice() function returns NULL if the choice was invalid. Nothing wrong with that, it's a common pattern.
What you need to additionally do is comply with rule 2. Right now you've got
(*getMenuChoice(menu))(&system);
which is kind of a mouthful: you call getMenuChoice(), then immediately call the function that it returned a pointer to. Split this up into several lines:
MenuFunction fp;
fp = getMenuChoice(menu);
if(fp == NULL)
fprintf(stderr, "invalid choice\n");
else
(*fp)(&system);
Here, we capture getMenuChoice's return value in a new variable fp, and we test to make sure it's not NULL before we call it.
[P.S. In your case, you don't need to worry about my rule 3.]
To avoid 'this' seg fault event:
Check that the instance of the typedef contains other than NULL before calling the function via the instance of the typedef
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.
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
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;
}