C Board  

Go Back   C Board > General Programming Boards > C Programming

Reply
 
LinkBack Thread Tools Display Modes
Old 11-29-2007, 07:33 AM   #1
Registered User
 
Join Date: Nov 2007
Posts: 26
Question Troubleshooting Input Function

Hey Folks,

Anyone know why I'm getting an "incompatible types in return" error for :
Code:
 if (fgets_ptr == NULL) return -1;
in Dev C++ 4.9.9.2?

Even when I remove the line, the function still does not behave as intended. I'm trying to take an entry, clear the buffer, and keep taking entries until the user gets it right (string neither to long nor empty), and finally remove the newline if there is one. Can anyone see what's going on here? Thanks.

-SH

Code:
contact input ()                                                /* gets an entry from the user */
{
     contact entry = {'\0','\0','\0','\0','\0','\0','\0','\0'}; /* initializes the structure   */
     char *fgets_ptr;
     int length;
     
     do{
     printf ("\nEnter a new name.\n\n>");
     fgets(entry.name, sizeof(entry.name), stdin);
     fgets_ptr = fgets(entry.name , sizeof(entry.name), stdin);
     if (fgets_ptr == NULL) return -1;  
     while((junk = getchar()) != '\n' && junk != EOF);
                    if ( strchr(entry.name, '\n') == NULL ){     /* if overflow */
                    printf ("\nYour entry is too long, please try again.");
                    entry.name[0] = '\0';
                    }
                    } while (entry.name[0] == '\0' || entry.name[0] == '\n');
     length = strlen (entry.name);             
     if (entry.name[length - 1] == '\n'){
         entry.name[length - 1] == '\0';}                        /* replace \n */
     return entry;
}
SiliconHobo is offline   Reply With Quote
Old 11-29-2007, 07:42 AM   #2
Mysterious C++ User
 
Join Date: Oct 2007
Posts: 14,099
That's because your return type is a struct of type contact, but -1 isn't of type contact, is it?
Elysia is offline   Reply With Quote
Old 11-29-2007, 08:24 AM   #3
Registered User
 
Join Date: Nov 2007
Posts: 26
Hmmm, so what do I return if I want this to compile and run without crashing if fgets returns NULL?

Also, when I remove this line the code compiles but I get prompted for a name and must hit enter 3 times before I get prompted for a name again, regardless of whether my entry was empty or too long. It seems to be an infinite loop. Is there something wrong with my do-while syntax? Thanks for reading.

Last edited by SiliconHobo; 11-29-2007 at 08:53 AM.
SiliconHobo is offline   Reply With Quote
Old 11-29-2007, 08:51 AM   #4
Kernel hacker
 
Join Date: Jul 2007
Location: Farncombe, Surrey, England
Posts: 15,686
The solution depends on what how you use your function elsewhere.

One solution is that you pass in a pointer to a contact, and fill it in (unless it fails), then return a "success/fail" value.

Or you could allocate some memory with malloc for a contact, and if you then fail to read in the data, free the memory and return NULL.

By the way:
Code:
     fgets(entry.name, sizeof(entry.name), stdin);
     fgets_ptr = fgets(entry.name , sizeof(entry.name), stdin);
I guess you only want to read the name once, right?

--
Mats
__________________
Compilers can produce warnings - make the compiler programmers happy: Use them!
Please don't PM me for help - and no, I don't do help over instant messengers.
matsp is offline   Reply With Quote
Old 11-29-2007, 08:54 AM   #5
Registered User
 
Join Date: Nov 2007
Posts: 26
Yes, only once in each run through the do-while. Thanks. Now I only have to hit enter twice instead of 3 times :P Could it be something to do with :

Code:
while((junk = getchar()) != '\n' && junk != EOF);
?

Last edited by SiliconHobo; 11-29-2007 at 09:01 AM.
SiliconHobo is offline   Reply With Quote
Old 11-29-2007, 09:05 AM   #6
Kernel hacker
 
Join Date: Jul 2007
Location: Farncombe, Surrey, England
Posts: 15,686
Most certainly - you only need that if you don't have a newline at the end of the entry.name, so you need to swap place between the if-statement checking for newline and the while-loop reading any leftover data.

Also, perhaps you may want to indent your code so that your loops are visible [one advantage of learning to program in Python is that indentation is enforced - your code don't work right if you sloppily just put things wherever you feel like.]

--
Mats
__________________
Compilers can produce warnings - make the compiler programmers happy: Use them!
Please don't PM me for help - and no, I don't do help over instant messengers.
matsp is offline   Reply With Quote
Old 11-29-2007, 09:13 AM   #7
Jack of many languages
 
Join Date: Nov 2007
Location: Katy, Texas
Posts: 1,929
It was working just fine when it looked like this: http://cboard.cprogramming.com/showp...08&postcount=6

Compare any differences.

Todd
Dino is offline   Reply With Quote
Old 11-29-2007, 11:35 AM   #8
Mysterious C++ User
 
Join Date: Oct 2007
Posts: 14,099
The usual way for functions to return success/fail is typically that they take an argument (in this case, a pointer) to a "buffer" where it can store the information and returns bool, int, whatever to indicate success or failure.
Elysia is offline   Reply With Quote
Old 12-02-2007, 05:41 AM   #9
Registered User
 
Join Date: Nov 2007
Posts: 26
Thanks for the replies. Todd: I know you gave the exact working code but I wanted to write my own based on what I learned from your example rather than simply copy/paste. I have now got the get_input function working fine on its own. However, when I try to write another function that calls get_input repeatedlly with different prompt and pointer parameters for each field in the contact structure:

Code:
int get_input (char, contact);
Code:
contact input ()                                                /* gets an entry from the user */
{
     contact entry = {'\0','\0','\0','\0','\0','\0','\0','\0'}; /* initializes the structure   */
     contact *input_ptr;
     input_ptr = &entry;
     char prompt1[25] = "Please enter a new name.";

     get_input (prompt1, &input_ptr->name);
     
     return (entry);
}
     
int get_input (prompt, *input_ptr)
{
     char *fgets_ptr;
     char junk = 0;
     int length;
     
                do{
                printf ("\n%s\n\n>", prompt);
                 *fgets_ptr = fgets(*input_ptr, sizeof(*input_ptr), stdin); 
                 if (fgets_ptr == NULL) return -1;   
                    if ( strchr(*input_ptr, '\n') == NULL ){         /* if overflow */
                    printf ("\nYour entry is too long, please try again.\a");
                    while((junk = getchar()) != '\n' && junk != EOF);
                    *input_ptr[0] = '\0';
                    }
                } while (*input_ptr[0] == '\0' || *input_ptr[0] == '\n');
     length = strlen (*input_ptr);             
     if (*input_ptr[length - 1] == '\n'){
         *input_ptr[length - 1] == '\0';}                            /* replace \n */
}
I get a host of errors:

Code:
Compiler: Default compiler
Executing  gcc.exe... 
In function `input':
106: warning: passing arg 1 of `get_input' makes integer from pointer without a cast
106: error: incompatible type for argument 2 of `get_input'
At top level:
111: error: syntax error before '*' token
In function `get_input':
112: error: number of arguments doesn't match prototype
30: error: prototype declaration
118: error: `prompt' undeclared (first use in this function)
118: error: (Each undeclared identifier is reported only once
118: error: for each function it appears in.)
119: error: `input_ptr' undeclared (first use in this function)
119: warning: assignment makes integer from pointer without a cast

Execution terminated
I've been fiddling with this for a while and I really need a little bit of interpretation to understand what's causing these warnings/errors. I'm new to pointers so please go easy on me. Thank you again for the good advice.

-SH

Last edited by SiliconHobo; 12-02-2007 at 05:50 AM.
SiliconHobo is offline   Reply With Quote
Old 12-02-2007, 05:47 AM   #10
Mysterious C++ User
 
Join Date: Oct 2007
Posts: 14,099
No wonder. You're not getting the hang of the syntax. A couple of tutorials might be in place. Especially functions and pointers:
Functions: http://www.cprogramming.com/tutorial/c/lesson4.html
Pointers: http://www.cprogramming.com/tutorial/c/lesson6.html
Elysia is offline   Reply With Quote
Old 12-02-2007, 05:51 AM   #11
Registered User
 
Join Date: Nov 2007
Posts: 26
Okay. I read them but no light bulbs are coming on atm. If I could understand the errors that would probably help. The only one I'm familiar with is the undeclared variables error but I don't see why I'm getting it since the stated variables are declared as parameters in get_input.

Last edited by SiliconHobo; 12-02-2007 at 06:04 AM.
SiliconHobo is offline   Reply With Quote
Old 12-02-2007, 04:06 PM   #12
Jack of many languages
 
Join Date: Nov 2007
Location: Katy, Texas
Posts: 1,929
OK, let's go over function declarations, function calls and function definitions. The summary of all this will be that they have to match.

Your declaration is this:
Code:
int get_input (char, contact);
This says you are passing, by value, a single one byte characters, and a contact datatype (whatever that is - it doesn't really matter).

For function call is this:
Code:
get_input (prompt1, &input_ptr->name);
So, at run time, you will pass a copy of prompt1, which is the address of a char[25] array, and the address of the name field of the structure pointed to by input_ptr.

You've scored 0 for 2 so far.

Now, your function definition says this:
Code:
int get_input (prompt, *input_ptr)
Not even close. You have to supply a data type for both parms, and you haven't specified a data type for either.

3 strikes - you are out. (Actually, more bugs than that - but I won't dwell on something you don't understand with too critical an eye.)

Now, Going back to my first point that the function declaration, function call and function definition have to match. You need to decide if you want pass parms by value or by reference. Here's an example of the differences between the two.

Call by value: You tell the compiler to make a copy (essentially) of whatever variable you want to pass to a function. The compiler (well, it's not actually the "compiler", but the code generated BY the compiler - but I'll call it the compiler for discussion purposes) will make a copy of the "value" and place it on the stack. When the function gets control, it uses the copy of the value and cannot touch the original value. Here's a commented example:

Code:
#include <stdio.h>

int myfunc(char) ;  // function declaration 

int main() { 

	char mychar = 'Z' ; 
	int rc  ; // return code 
	
	// mychar will be Z 
	printf("main: before myfunc() call, mychar = %c\n", mychar) ; 

	// Call myfunc(), using a call be value syntax.  A copy of the 
	// actual value of mychar will be placed on the stack.  myfunc() will
	// never have access to the original mychar variable.  
	rc = myfunc(mychar) ;   // function call 
	
	// Print out the value of mychar.  It will still be Z, because 
	// function myfunc() changed the copy of mychar that was placed 
	// on the stack.   This now proves that function myfunc() did not 
	// use the original value of mychar - it only changed a temporary 
	// copy of it that was on the stack. 
	printf("main: after myfunc() call, mychar = %c\n", mychar) ; 

	return 0 ; 
} 

int myfunc(char xxx) {   // function definition 
	// report the incoming value. 
	printf("xxx should be Z, and it is: %c \n", xxx ) ; 

	// Change variable xxx to a new value.
	xxx = 'H' ; 

	// This shows that the value was changed. 
	printf("xxx should be H, and it is: %c \n", xxx) ; 
	return 0 ; 
}
Maybe the most important thing to point out from the above example is that the function myfunc() cannot change the calling routine's variable in a call-by-value scenario.

Call by reference: Let's change our example just a little now to a call-by-reference call. We need to use a pointer variable and change our function declaration, call and definition. The primary differences are in red.

Code:
#include <stdio.h>

int myfunc(char *) ;  // function declaration 

int main() { 
	char mychar = 'Z' ; 
	char * myptr ; 
	int rc  ; // return code 

	myptr = &mychar ;   // Initialize myptr pointer to the address of mychar.

	// mychar will be Z 
	printf("main: before myfunc() call, mychar = %c\n", mychar) ; 
	printf("main: before myfunc() call, myptr  = %p\n", myptr) ; 

	// Call myfunc(), using a call be reference scheme.  The pointer to the 
	// actual value of mychar will be placed on the stack.  myfunc() can 
	// now access the original mychar variable.  
	rc = myfunc(myptr) ;  // function call  
	
	// Print out the value of mychar.  We passed a pointer to mychar, 
	// and function myfunc() changed the value of it through indirection. 
	printf("main: after myfunc() call, mychar = %c\n", mychar) ; 
	printf("main: after myfunc() call, myptr  = %p\n", myptr) ; 

	return 0 ; 
} 

int myfunc(char * xxx) { // function definition 
	// report the incoming value. 
	printf("xxx value should be Z, and it is: %c \n", *xxx ) ; 
	printf("xxx pointer is is: %p \n", xxx ) ; 

	// Through pointer xxx, variable the value to a new value.
	*xxx = 'H' ; 

	// This shows that the value was changed. 
	printf("xxx value should be H, and it is: %c \n", *xxx) ; 
	return 0 ; 
}
Now, let's focus on another aspect of getting all three (function declaration, call and definition) to match - the return value.

Here's the first example repeated to focus on the the return type.
Code:
#include <stdio.h>

int myfunc(char) ;  // function declaration 

int main() { 

	char mychar = 'Z' ; 
	int rc  ; // return code  
	
	// mychar will be Z 
	printf("main: before myfunc() call, mychar = %c\n", mychar) ; 

	// Call myfunc(), using a call be value syntax.  A copy of the 
	// actual value of mychar will be placed on the stack.  myfunc() will
	// never have access to the original mychar variable.  
	rc = myfunc(mychar) ;   // function call 
	
	// Print out the value of mychar.  It will still be Z, because 
	// function myfunc() changed the copy of mychar that was placed 
	// on the stack.   This now proves that function myfunc() did not 
	// use the original value of mychar - it only changed a temporary 
	// copy of it that was on the stack. 
	printf("main: after myfunc() call, mychar = %c\n", mychar) ; 

	return 0 ; 
} 

int myfunc(char xxx) {   // function definition 
	// report the incoming value. 
	printf("xxx should be Z, and it is: %c \n", xxx ) ; 

	// Change variable xxx to a new value.
	xxx = 'H' ; 

	// This shows that the value was changed. 
	printf("xxx should be H, and it is: %c \n", xxx) ; 
	return 0 ; 
}
Declaration: For myfunc(), the return type of int is declared in the function declaration.
Definition: myfunc() repeats the same "declaration" again in the definition, stating it will return an int value. And, indeed, it issues a return 0 ; before it exits.
Call: When called, the call needs to do something with int that is returned from the call to myfunc(). It does this by assigning the value to the variable rc. Again, all threes match.

Also, note the green code above. The exact same thing is being done for main() - we just don't have a function declaration for main().

Hope this helps.

Todd

Last edited by Dino; 12-02-2007 at 04:08 PM.
Dino is offline   Reply With Quote
Old 12-03-2007, 02:16 AM   #13
Mysterious C++ User
 
Join Date: Oct 2007
Posts: 14,099
Syntax for function are as following (see Todd's example too of course):
Declarations:

Code:
ReturnType FunctionName(ArgumentType ArgumentName, ArgumentType2 ArgumentName2);
ReturnType is what type you want the function to return, FunctionName is the name of your function that you'll use to call it, ArgumentType is the type of argument you want the function to accept and ArgumentName is the name by which you'll access that varaible.
Function definition is the same:

Code:
ReturnType FunctionName(ArgumentType ArgumentName, ArgumentType2 ArgumentName2)
{
/* Your code here */
}
Your delcaration and definition MUST match. The declarations tell the compiler that your function exists. If your declaration and definition mismatch, you'll get a compiler error or linker error.

This should also help.
Elysia is offline   Reply With Quote
Old 12-05-2007, 07:14 AM   #14
Registered User
 
Join Date: Nov 2007
Posts: 26
Okay, thanks Todd and Elysia for the detailed explanation. I've simplified the program and I believe my declaration, call and function now match.

Code:
char get_input (char[26]);
Code:
get_input (prompt1);
Code:
char get_input (char prompt[26])
I have changed the return type to char so I can get the string in get_input and then return it to the calling function which can assign it to the appropriate place in the structure. This is what I've written:

Code:
contact input ()                                                /* gets an entry from the user */
{
     contact entry = {'\0','\0','\0','\0','\0','\0','\0','\0'}; /* initializes the structure   */
     char prompt1[26] = "Please enter a new name.";
     char prompt2[26] = "Enter a street address.";
     char prompt3[26] = "Enter a city.";
     char prompt4[26] = "Enter a province.";
     char prompt5[26] = "Enter a post code.";
     char prompt6[26] = "Enter a country.";
     char prompt7[26] = "Enter a date of birth.";
     char prompt8[26] = "Enter a phone number.";
     char prompt9[26] = "Enter an email address.";

     entry.name = get_input (prompt1);
     entry.add.street = get_input (prompt2);
     entry.add.city = get_input (prompt3);
     entry.add.state = get_input (prompt4);
     entry.add.post = get_input (prompt5);
     entry.add.country = get_input (prompt6);
     entry.birth = get_input (prompt7);
     entry.phone = get_input (prompt8);
     entry.email = get_input (prompt9);
     
     return (entry);
}

char get_input (char prompt[26])
{
     char string [50] = {0};
     char junk = 0;
     int length;
     
                do{
                printf ("\n%s\n\n>", prompt);
                fgets(string, sizeof(string), stdin);   
                    if ( strchr(string, '\n') == NULL ){         /* if overflow */
                    printf ("\nYour entry is too long, please try again.\a");
                    while((junk = getchar()) != '\n' && junk != EOF);
                    string[0] = '\0';
                    }
                } while (string[0] == '\0' || string[0] == '\n');
     length = strlen (string);             
     if (string[length - 1] == '\n'){
         string[length - 1] == '\0';}                            /* replace \n  */
     return (string);
}
I'm getting "incompatible types in assignment" error on the lines that call get_input. What is the correct way to plug the returned string into the contact structure?

Code:
typedef struct address_tree             /* address sub-strcuture */
{
       char street[50];
       char city[50];
       char post[50];
       char state[50];
       char country[50];
}address;   

typedef struct top_tree                 /* top-level database structure */
{
       char name[50];
       char email[50];
       char birth[50];
       char phone[50];
       address add;
}contact;
Also, is there any practical advantage to using a nested structure here? I just just did it to learn how they work. Thanks for reading.
SiliconHobo is offline   Reply With Quote
Old 12-05-2007, 07:18 AM   #15
Mysterious C++ User
 
Join Date: Oct 2007
Posts: 14,099
There is definetly an advantage - to your program's layout. It would make sense to have your address with all the other information. And making a struct out of that can also be helpful.
For your error, you declare return type as char but you return char* (just for reference, returning an array for sort automatically casts it to a pointer, so a char array becomes char*).
What is the solution? There are several, but the one that's most recommended is that get_input takes a char* argument to a buffer to fill. And it should take a size argument too, to make sure that it doesn't input more data than space available in the buffer.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
Undefined Reference Compiling Error AlakaAlaki C++ Programming 1 06-27-2008 11:45 AM
dllimport function not allowed steve1_rm C++ Programming 5 03-11-2008 03:33 AM
airport Log program using 3D linked List : problem reading from file gemini_shooter C Programming 3 03-04-2005 02:46 PM
c++ linking problem for x11 kron Linux Programming 1 11-19-2004 10:18 AM
Contest Results - May 27, 2002 ygfperson A Brief History of Cprogramming.com 18 06-18-2002 01:27 PM


All times are GMT -6. The time now is 03:47 AM.


Powered by vBulletin® Version 3.8.1
Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.3.0 RC2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22