Function pointer points to nothing — how do I avoid this? - c

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

Related

Trying to pass a value to a function that takes a pointer as argument

Ive been searching far and wide for the answer but havent been able to come op with a viable solution. As you can see in my code beneath, I'am trying to pass the value of choice into get_user_input(char *input). I think this is what i want to do because i need the function scanf to be called inside get_user_input(char *input).
I'am fairly new to programming and have alot of trouble trying to understand pointers and references
I hope someone could help me out!
The get_user_input(char input) function
void get_user_input(char *input) {
*input = '\0' ;
scanf(" %c", input);
}
Trying to call scanf() from the function get_user_input(char input)
void manual_read_sensors(void) {
while (1) {
// Ask the user for which sensor to read.
printf("Which sensor do you want to read?\n"
"(i)ntensity\n"
"(a)ngle\n"
"(t)ime\n"
"(s)unscreen\n"
"(q)uit\n"
"Enter choice: ");
void choice = get_user_input(); <----- Trying to call
// Return to the main menu again.
if (choice == 'q')
break;
First, your get_user_input function accepts a char * and doesn't return anything, but you are not passing anything - You should pass the address of choice like: get_user_input(&choice);. If you pass the address of choice, the get_user_input function will be able to write data to it.
Second, void choice is not a valid declaration. Although you can declare a void *choice. In this case, you should declare choice as a char: char choice;.
Third, get_user_input does not return anything and you should not try to assign the result of the function to a variable.
Fourth, *input = '\0' ; does not achieve anything as in the next step, you overwrite input with scanf.
get_user_input should look like this:
void get_user_input(char *input) {
scanf(" %c", input);
}
manual_read_sensors should look like:
void manual_read_sensors(void) {
char choice;
while (1) {
// Ask the user for which sensor to read.
printf("Which sensor do you want to read?\n"
"(i)ntensity\n"
"(a)ngle\n"
"(t)ime\n"
"(s)unscreen\n"
"(q)uit\n"
"Enter choice: ");
get_user_input(&choice);
// Return to the main menu again.
if (choice == 'q')
break;
You have declared that get_user_input() function takes a char reference as its argument. But when this function was called from manual_read_sensors() function, there was no reference passed to get_user_input() function.
choice variable cannot be declared as type void because this is not allowed in C/C++ language (reference).
Rather try declaring,
char choice;
Since get_user_input() function is declared as having void as return type, it cannot return any value to other function and hence, a variable cannot be assigned a value like so,
char choice = get_user_input(); //error
Since get_user_input() does not return any value, we need to use pointer variable for manipulating choice variable. But the pointer variable also requires the address of the variable to point to, that's why we need to pass the reference (or address) of choice variable to get_user_input() function. Like so,
get_user_input(&choice);

Function changes data on other variables

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.

Local character array in function retains its value from previous calls

The first call to printf in the number() function prints the string that was in
token[] at the end of its last call. Can anyone explain the reasoning behind this?
I deleted most of the function commands that are not related to the string. If I needed to provide more code let me know.
PS : If I replace the token array with a char * and dynamically store and free the address I have no such issue
void number(FILE *fp, FILE *fo, char ch)
{
char token[100],*tmp; //characters read are saved here
State currentState = S0;
State prevState;
int i,counter = 0;
printf("THE PREVIOUS TOKEN = %s\n\n",token);
while(1)
{
switch (currentState)
{
case S0 :
{
...
}
case S1 :
{
...
}
case S2 :
{
...
}
case S3 :
{
...
}
case S4 :
{
...
}
case S5 :
{
...
}
case INTEGER :
{
...
}
case FLOAT :
{
...
}
case BAD :
{
dbg(" BAD\n");
prevState=BAD;
fprintf(fo,"+Error! Invalid syntax for an integer or float\n");
tmp=avoidchars(fp,ch);
if(tmp)
{
printf("Unknown token : %s\n\n",strcat(token,tmp));
free(tmp);
}
break;
}
}
if ( ( currentState==GOOD ) || ( currentState==BAD ) && ( prevState == BAD ) )
break;
if( currentState != INTEGER && currentState != FLOAT && currentState != BAD)
{
token[counter] = ch;
ch=fgetc(fp);
}
counter++;
}
}
Printing an uninitialized variable is undefined behavior, so you are essentially asking "how undefined is undefined behavior".
There are no guarantees of what will happen when you print uninitialised data: you might get garbage, you might get something that seems to make sense, you could get a program crash. You might get something printed that makes sense today and have the program crash tomorrow. The program might work fine on your machine but crash at your customer's machine.
There are no guarantees and no predictable behavior.
Behind the scenes of your specific system, it is likely that the array is stored on the stack. If you are lucky, that area of the stack is not modified between function calls, why it might seem as the memory is preserved. It is a bad idea to design a program which relies on luck though. If you actually need to preserve the array, it needs to be declared static, which will also force an initialization to a predictable value.
Uninitialized non-static local variables have an indeterminate value, doing just about anything with such a variable, except to initialize it, leads to undefined behavior. You should simply not do it.
But to explain the behavior here, think about that the compiler doesn't initialize these variables for you, they are after all uninitialized. However, if you call a function multiple times after each other this non-initialization policy simply means that the memory will stay the same between calls. If you call functions that have their own local variables which they modify between the calls to your function, then the second call to your function will have your local variables contain the value from the previous call to the other function.
Try for example
number(...);
printf("%s %s %s %s %s\n", "foo", "bar", "flux", "blam", "foblax");
number(...)
Now if you print out the token array in the second call (technically undefined behavior), the contents will be quite different from the contents left by the previous number call.

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.

passing pointer to recursive function in C

I'm just starting on the road the learning C, and ran into some difficulty:
The code listed below is giving me the following error:
Attaching to program: `/workfolder/cocoa/c_stuff/bookshelf/build/Debug/bookshelf', process 1674.
Cannot access memory at address 0xa0df194
Cannot access memory at address 0xa0df194
// code start
#define MAX_NAME_LENGTH 200
#define MAX_AUTHOR_LENGTH 200
#define MAX_DESCRIPTION_LENGTH 1000
#define MAX_PUBLISHER 200
#define MAX_ISBN 50
//structures<
typedef struct {
char title[MAX_NAME_LENGTH];
char author[MAX_AUTHOR_LENGTH];
char ISBN[MAX_ISBN];
char description[MAX_DESCRIPTION_LENGTH];
char publisher[MAX_PUBLISHER];
} Book;
void getUserInput(Book *s[])
{
printf("what is the book's title ?\n");
fgets(s[book_count]->title, MAX_NAME_LENGTH, stdin);
printf("what is the author's name?\n");
fgets(s[book_count]->author, MAX_AUTHOR_LENGTH, stdin);
printf("what is the ISBN?\n");
fgets(s[book_count]->ISBN, MAX_ISBN, stdin);
printf("write a short description\n");
fgets(s[book_count]->description, MAX_DESCRIPTION_LENGTH, stdin);
printf("what is the book's publisher\n");
fgets(s[book_count]->publisher, MAX_PUBLISHER, stdin);
printf("want to add another book ? Y\\N\n");
book_count++;
if(tolower(fgetc(stdin)) == 'y')
{
return getUserInput(s);
}
else
{
return;
}
}
int main (int argc, const char * argv[]) {
// insert code here...
Book *book_shelf[100];
if((book_shelf[0] = (Book *)malloc(sizeof(Book))) == NULL)
{
exit(1);
}
getUserInput(book_shelf);
return 0;
}
The code compiles properly, and the function runs fine the first time (all the questions get asked and the struct receives the data); but when the user types 'y' to add another book, the mem error occurs.
Any ideas where the error is happening?
Thanks in advance!
You've only ever allocated memory for the first book in main - after that it tries to write to the next slot in the array, which doesn't point to an allocated block of memory, giving you a seg-fault. You're going to have to allocate memory for each book you want to read in.
In addition, since C doesn't know how long an array is, you have to pass that information along into function calls. (And I don't see where you're defining book_count.)
You might try something along these lines:
void getUserInput(Book *s[], int *book_count, int max_book_count)
{
if (book_count == max_book_count) return; // If we've filled all the slots, we can't add anymore without causing trouble.
s[book_count] = malloc(sizeof(Book));
..
if(tolower(fgetc(stdin)) == 'y')
{
(*book_count)++;
getUserInput(s, book_count, max_book_count);
}
return;
}
int main (int argc, const char * argv[]) {
// insert code here...
Book *book_shelf[100];
int book_count = 0;
getUserInput(book_shelf, &book_count, 100);
// Make sure to free all the malloc'd data
}
Even better in this situation, would just be using a loop and skipping the whole recursion step.
int main (int argc, const char * argv[]) {
// insert code here...
Book *book_shelf[100];
char response = 'y';
int book_count = 0;
while (book_count < 100 && response == 'y')
{
book_shelf = malloc(sizeof(Book));
response = getUserInput(book_shelf[book_count++]);
}
// make sure to free all the allocated data!
}
char getUserInput(Book *book)
{
// write input straight to book
printf("what is the book's title ?\n");
fgets(book->title, MAX_NAME_LENGTH, stdin);
...
return tolower(fgetc(stdin));
}
Unless I'm reading something wrong, you haven't defined book_count before using it as an array subscript.
Within main, you allocated on the stack an array of 100 pointers to the Book Structure. I believe it was your intent to allocate 100 structures and then pass the address to that block of structures to getUserInput
Change main to:
Book book_shelf[100];
...
getUserInput(book_shelf);
...
EDIT: OOPS Missed the single Book malloc mentioned in the earlier post. That ones Correct for the first book. If you edit as above and eliminate the
if (book_shelf[0]...) check, you'll accomplish your intended results
You allocate just space for the firstbook, not for the others (malloc in main)
I guess there is some code missing, no declaration and initialization of book_count
You should use loops instead of recursion
Use not recursion but loops for this kind of repetition
Recursion is probably overkill for this problem where a simple do { ... } while(user keeps answering yes) would do. However the problem you having is in main with your Book *book_shelf[100]. There are several ways you could solve this problem.
First change it to an array of Book's like samills suggests:
Book book_shelf[100];
and then change your getUserInput to something like this:
getUserInput(Book *book_shelf, int offset, int length) {
if(offset < 0 || offset >= length) {
return;
}
//...
return getUserInput(book_shelf, offset + 1, length)
}
Or you could use your existing code and change you getUserInput function to look something like this and remove the malloc from main:
getUserInput(Book *book_shelf) {
book_shelf[book_count] = (Book*)malloc(sizeof(Book));
// ...
}
props for correct use of the sizeof operator (I see that thing misused so often it makes my eyes bleed).
As in Josh's answer, by adding the following lines to your code should make it work:
book_count++;
if(tolower(fgetc(stdin)) == 'y')
{
if((book_shelf[book_count] = (Book *)malloc(sizeof(Book))) == NULL)
{
printf("Cannot allocate memory for Book");
exit(1);
}
return getUserInput(s);
}
else
{
return;
}
However, I encourage you not to use the recursive function for getting input. Recursive can lead to difficulties in debugging. You may consider using normal loop instead.
Note: I'm assuming the book_count is global variable which has been initialized to 0
thanks a lot for the replies!
I realized that I hadn't malloc-ed enough memory to handle more then one element of the struct array (Exactly what Josh is saying). So essentially:
Book *book_shelf;
if(book_shelf = (Book*)malloc(sizeof(Book)) == NULL)//exit code
so the second time around I would hit a memory issue.
thanks again!
Looks like your still doing it wrong:
Book *book_shelf;
if(book_shelf = (Book*)malloc(sizeof(Book)) == NULL)//exit code
book_shelf is only the size of a pointer. When you do the malloc you only allocate one Book at a time. This is wrong. You need to allocate contiguous memory for an array of Book objects all in one instanciation of an array.
Like
Book book_shelf[100];
not
Book *book_shelf[100];
or using malloc, use your pointer to point to an array instanciated using
100*malloc(sizeof(Book)).
You may get lucky that no other heap memory is allocated in between your malloc(sizeof(Book)) calls and that the memory management system is alocating contiguous memory by default. Also, book_shelf will only point to the last malloced Book structure, not the first one as you indicated you want in your original question.
Josh is also not allocating enough memory at one time. Use a linked list if you want to keep extending elements to the end of your book_shelf one-by-one.
factorial with pointer and recursion
#include<iostream.h>
#include<conio.h>
int show(int *p)
{
int f;
int x=*p;
if(*p==1) //boundry checking for recursion
return 1;
else
f=x*show(&(--*p)); //this code is similar to f=x*show(n-1); with non-pointers
return f;
}
void main()
{
int a=6;
int b=show(&a);
cout<<b;
getch();
}

Resources