Scanf taking incorrect input - c

This is the only block of code where scanf doesn't seem to pick up the entered values correctly.
It always works as designed the very first time it executes the code. When it asks the second time (after executing newGame once) no matter what you type it always takes the value 1 and then on the second iteration of the infinite loop it works correctly. Using pointers didn't help like I thought it would.
Edit: updated the code for new readers, this doesn't work either
void newGame(void){
int option;
puts("Would you like to play a new game? 1: Yes 2: Return to the main menu");
while (1) {
scanf("%d", &option);
switch(option) {
case 1:
newGame();
break;
case 2:
return;
default:
printf("Invalid input %d!\n", option);
break;
}
}

If I understand the observed misbehavior correctly, it manifests like this (for example):
The function emits the prompt
You type "1" and hit [enter]
The function emits the prompt
You type "2" and hit [enter]
Nothing seems to happen
You type "2" and hit [enter]
Control passes to the next statement after the initial call to newGame()
At least, that's an observable behavior of the function presented. Interpreting that to describe the problem assumes that the "Nothing seems to happen" at (5) is what the question describes as "no matter what you type it always takes the value 1". I find it hard to reconcile those, but I assume that the code presented actually manifests the problem, so I have to somehow connect the claim to the actual behavior.
That particular behavior (having to type "2" twice or more to return to the main menu) arises from the recursive implementation of the function. It goes like this:
Control enters the function on the initial call to it.
The function prints the prompt.
The function enters the while loop, where:
it scans the user's choice (and will run into trouble either then or later if the user has entered anything non-numeric);
the user having entered "1", the function calls itself recursively, in which new execution it
prints the prompt,
enters the while loop,
scans the user's input ("2" this time),
returns from the recursive call.
(back in the top-level newGame() execution) the function reaches the bottom of the while loop and loops back to scan a new input (another "2" in the example session).
The function returns from the top-level execution of newGame().
The simplest way to revise the code so that the user only needs to select option "2" once would be to add a return statement immediately after the recursive call to newGame(). The recursive call will not return until the user has chosen option "2", so there is no need to loop back for more input in that case.
But the best solution would be to structure the menu system to be non-recursive. In real-world programs, recursion is almost never the right answer. Even in this case, the recursive approach puts a (system dependent) limit on how many games can be played in a row before the program overflows its stack. That might be in the hundreds or perhaps even thousands, but there doesn't need to be any limit at all.

You need to check the return value of scanf to ensure that the input value was actually a number -- if it is not, scanf will return 0 and not read anything. It also might return EOF if an end-of-file is reached. So you want something like:
if (scanf("%d", &option) != 1) {
if (feof(stdin)) {
printf("Unexpected end of file");
exit(0); }
printf("Unexpected character %c in input", getchar());
continue; }
in your while loop to read your number.

Related

Infinite loop with 'char' type input

I am learning binary search tree. Given below is the main function of a program to perform basic BST operations. The option variable chooses which operation to perform for switch
int main()
{
struct node* tree=NULL;
struct node* ptr=NULL;
struct node* ptrm=NULL;
int val;
int option;
do
{
printf("\n1.Insert Node\n2.Preorder Traversal\n3.Postorder Traversal\n4.Inorder Traversal\n5.find_smallest_element\n6.find_largest_element\n7.Delete Element\n8.Total_nodes\n9.total_external_nodes\n10.total_internal_nodes\n11.Mirror image\n12.Exit\n");
printf("\nEnter option");
scanf("%d",&option);
switch(option)
{
case 1:
printf("\nEnter value to be inserted");
scanf("%d",&val);
tree=insert_element(&tree,val);
printf("\n%d Inserted\n",val);
break;
case 2:
preorder(&tree);
break;
case 3:
postorder(&tree);
break;
case 4:
inorder(&tree);
break;
case 5:
ptr=find_smallest_element(&tree);
printf("\nSmallest element:%d",ptr->data);
break;
case 6:
ptr=find_largest_element(&tree);
printf("\nLargest element:%d",ptr->data);
break;
case 7:
printf("\nEnter value of element to be deleted");
scanf("%d",&val);
tree=delete_node(&tree,val);
break;
case 8:
printf("\nTotal nodes%d",total_nodes(&tree));
break;
case 9:
printf("\nTotal External nodes%d",total_external_nodes(&tree));
break;
case 10:
printf("\nTotal Internal nodes%d",total_internal_nodes(&tree));
break;
case 11:
ptrm=mirror_image(&tree);
}
}while(option!=12);
return 0;
Everything works fine when i give int data as input for 'option'.However, when i give a char input the program goes into infinite loop and displays option list repeatedly.
Why does this happen?
Since you used %d format specifier in the scanf() format string,
scanf("%d",&val);
will successfully assign to val only if an integer was given as the input. If a char is given instead, scanf() (which returns the number of successful assignments) will return 0 here and will leave the char in the input buffer unconsumed.
During the next iteration of the loop, this char would still be in the input buffer and scanf() would end up trying to read the same thing and will won't assign to val once again.
This will go on and on resulting in an infinite loop.
To solve this, check the value returned by scanf(). If it is not 1, clear the input buffer till the next \n (newline) like
int t;
while( (t=getchar()) != `\n` );
This will consume the old data till a \n from the input buffer.
You could then use the continue statement to skip the rest of that iteration of the loop.
Read about getchar() here.
Why does this happen?
The roots of this issue stem all the way back to how scanf indicates error codes to your code (not at all, because your code discards them), and what scanf("%d", &val) is expected to do when non-decimal input is encountered; it stops reading input, possibly returning an error code, but your code discards that and continues on merrily trying to delete the node indicated by the value which may not have been read, leading to possible use of an uninitialised variable later...
Some people take the guessing to an extreme, and think it's appropriate to use fflush(stdin) to solve this (it isn't; don't do that...). You've not gone that far, but I think it might be a good idea to start reading the manuals of the functions you're using. The scanf manual is here. Make note of that URL, and realise that you can look up other standard functions (both C99 and POSIX standard) by substituting the name of the function.
The first thing your code must do is check that return value, which your manual will document in the RETURN VALUES section; as with most standard library functions, scanf has a return value which your code should most likely contain critical logic regarding! From there, how you handle errors is your business. Perhaps it might be appropriate to use something simple yet user-unfriendly, like:
perror(scanf);
exit(EXIT_FAILURE);
You should seek the simpler solutions where possible, to avoid overcomplicating things. If your input doesn't come directly from the user, or you just want to prototype, you should use the solution above. You can always change exit(EXIT_FAILURE) to return EXIT_FAILURE; and return 0; on success, if necessary later.
If you choose to keep your program running, how much of the user input gets discarded due to the typo is up to you. By far the simplest option is to just read a single character (using getchar();)...
You could choose to discard a word of input, like so: scanf("%*s");. The * informs scanf to read and discard the input, rather than reading and assigning.
Neither of those options strike me as being particularly user-friendly. If you're going to the effort of making a user-friendly interface, you'll probably want to choose one of the following options.
Using the * assignment-suppression modifier, you can also discard a line of input, like so:
scanf("%*[^\n]");
getchar();
The getchar(); is necessary to discard the newline character, which we expect to be discarded when a line is discarded.
Using the command line arguments for your input, rather than using stdin (or other files/streams). Some surprisingly simple yet versatile menus have been produced this way, such as the ones your compiler presents to you. Your mode of input then changes to using friendlier functions such as sscanf, and developing your program not as a looping program that remains open, but as an instant program which gets executed every now and then, when necessary, to update records or what-not.
Using the graphical user interface instead of the console. Well, that one really makes the ol' noggin' flog, eh? You could use... a context menu such as the File/Edit/etc menus in Windows, or a listbox (which would be more touch-screen friendly) to prompt your user for a selection.
Suffice to say, this looks like homework, so you probably don't have the choice to design a more appropriate user interface... In this case, I suggest using the * assignment-suppression modifier as per above (the first bolded section).

C Ending a loop with user input

I'm trying to write a program that lets the user insert integer values into an array until they enter a specific key to stop, say ENTER, or X.
int array_numb[100];
char quit = 'x';
printf("Enter as many values into the array as you want, pressing x will end the loop\n");
while(1==1){
int i = 0;
scanf("%i",&array_numb[i]);
i++;
array_numb[i] = quit;
}
I know it's wrong but this is my thought process. Any ideas? thanks for the help
Enter as many values into the array as you want while having int array_numb[100];, at some point you will be in trouble. What if the user enters more than 100 numbers before entring the sentinel value? You'll be overruning the allocated memory, isn't it?
You should check the return value of scanf() to ensure proper input. A mismatch in the supplied format specifier and the type of input will cause scanf() to fail. Check the man page for details.
You need to have a break statement inside while(1) loop to break out. Otherwise, you need to have a conditional statement in the controlling expression for while() loop to terminate the loop.
Note: IMHO, the best way to stop scanning user input is to comapre the return value of scanf() against EOF, which can be produced by pressing CTRL+D on linux and CTRL+Z in windows.
You can check this answer for your reference.
The same thing can be achieved as shown below:
scanf() can fail and check the return value. When a character is scanned scanf() fails
int i=0;
printf("Enter as many values into the array as you want, pressing x will end the loop\n");
while(i<100 && scanf("%d",&array_numb[i])==1)
{
i++;
}
scanf, with %i, will fail to scan a number when invalid data, such as a character is inserted and will return 0(in your case) if it fails.
Just check the return value of scanf. If it is zero, use
if(getchar()=='x') //or if(getchar()==quit)
break;
Note: Clear/flush the standard input stream(stdin) using
int c;
while((c=getchar())!='\n' && c!=EOF);
after the loop as well as after the break; .
You could learn something and use signals:
http://man7.org/linux/man-pages/man7/signal.7.html
Register SIGINT, override it, let it check which key was pressed, then let it change a variable. In your loop check if this variable is 0 or 1.
Signals have top priority, signal code will be executed no matter the loop.
Example: http://www.thegeekstuff.com/2012/03/catch-signals-sample-c-code/
It's overkill for this case but a fun thing to know. Signals are the way how an OS is managing events, like process kills, keyboard input etc. Be warned, how to use this is OS specific and has not much to do with C itself.

C: combine return & scanf() in one line

I'm writing a program in C that displays a menu that asks the user to make a decision.
I want to bring up the menu multiple times in the program so I put it into function.
Each choice has a number associated with it:
(1) Add
(2) Subtract
The function displays the menu and then then I scanf the int response to a local variable and then return the variable (I of course declare the variable at the beginning of the function).
int function ()
{
int choice;
// *insert print menu code here*
scanf(" %i", &choice);
return choice;
}
Can I make this shorter by somehow doing a: return scanf(" %i", stdin);
I'm not sure if stdin would be the right choice, but that's what searching tells me.
Sure you can:
int scanInt(void) {
int result;
return (scanf(" %i", &result), result);
}
But is it useful? Probably not.
Note that this might look like undefined behaviour at first glance, because you're writing and reading from the same memory location in one expression. But it is not, as the comma operator introduces a sequence point.
You can't do this with scanf: you'd be returning the return value of scanf, which only indicates the number of items successfully read. If you need to return choice then you'll need to call scanf to fill choice and return it in two separate steps.
Another point here is that you should check the return value of scanf: it will fail if it's unable to convert the input. In your code, scanf will fail if the input is not an integer.
Furthermore, scanf reads from stdin - standard input - by default, you can't pass that as an argument unless you're using fscanf, which otherwise behaves in the same way.
Have you looked at using fgets for your input? It does return the next input line, which would allow you to do the read in the return statement since fgets returns the next line of input as a string. But you'd still need to parse the input yourself, which should probably happen within your function. In any case, fgets is typically a better choice for user input because it separates reading the input from parsing it, and avoids problems when using scanf due to input that doesn't match your format string.
You can, sort of -- but don't.
scanf returns the number of items scanned. You can't make it return the value of one of the scanned items.
return scanf(" %i", &choice), choice;
This uses the comma operator (not to be confused with the comma delimiter between function arguments), which evaluates both operands and yields the result of the right operand.
But there is no good reason to do this. There is no great virtue in making your source code more compact like this. The multi-line form is clearer, and it's easier to modify by adding error handling (what happens if the user enters something other than a number?)
Or you could write your own function that calls scanf and returns the value you want, but that still doesn't address the issue of error handling.
And if your goal is simply to put them on one line:
scanf(" %i", &choice); return choice;
But again, this is not an improvement.
No, you can't with scanf. The function needs a memory location to store the result, and doesn't return it. So you just have to live with the extra code.

Infinity loop in while statement

Here is a snippet from my code
scanf("%d", &s);
while(s!=1 && s!=2) {
if(scanf("%d", &s) != 1) {
show_warning(); //just print some info msg
}
}
The idea is to execute show_warning function only if user enter something different of 1,2 and entered value to be only integer.
With the code above it jumps in infinity loop. How to fix that ?
The problem is that the failed input operation doesn't extract any characters from the stream (and you'll keep reading the invalid input over and over), so you have to empty the input manually. For example:
char buf[1000];
// ...
if(scanf("%d", &s) != 1)
{
puts("Error, try again: ");
fgets(buf, 1000, stdin);
}
As I suggested in the other question, if you use fgets from the start to always read one line and process it later, you overcome this problem.
(The same philosophy is true in C++: read the whole line first so the input stream can move on, and process the line later to see if its valid.)
Why are you using a while loop? Do you want show_warning(); to be called once, or repeatedly? If you answered once then you only need an if-statement.
What makes you think it is looping until infinity?
You only get the warning message when you have not entered a number because it did not read a token.
Changing the && to || though is a certain way to ensure it will run to infinity as your loop will never break then.
You might want an alternative message to prompt the user to enter data when they did enter a number but not 1 or 2?
I'm assuming you're having issues when a non-integer is input. This is because scanf leaves non-matching characters in the buffer, so subsequent calls are seeing the same input and looping.
What you need to do is if the scanf call returns any number other 1 is: read that character in using scanf("%c",&somechar) so you can print it and tell the user that it isn't accepted. The non-accepted input will then have been removed so the next call to scanf will give you the next input rather than the one you saw on the previous iteration.
Also what happens when no scanf succeeded? The variable s remains unchanged (and perhaps with an undefined value, if you did not initialize it). You should set it, or change the condition of the while !
And you really should learn to compile with warnings enabled and debugging information (i.e. gcc -Wall -g on Linux) and to use a debugger (e.g. gdb on Linux)

scanf() is getting skipped

Hi
Just started learning c for uni (usually use objective c) and have run into a strange issue with scanf, i have the following code
while(stringCheck == 0){
scanf("%c",&computerType);
computerType = toupper(computerType);
if ( computerType == 'L') {
/*set stringCheck to 1 so the scanf while loop breaks*/
stringCheck = 1;
counter = 0;
} else {
printf("ERROR\n");
}
}
This i printing out "ERROR" then asking for input (so it is skipping the scanf statment the first time). If i change the it to another variable that is a string it works fine, it stops on the first time.
The rest of the code works fine, its just the fact that it print an error as soon as it enters the loop that is annoying.
I have tried getChar() and it does the same thing.
Thank you for any help you have to offer.
If it's printing an error the first time you enter the loop, then there's already something in the input buffer. I'll guarantee (assuming your compiler is not brain-dead) that it is not skipping the scanf. You should check what it's actually receiving by changing:
printf("ERROR\n");
to:
printf("ERROR, code = %02x\n", computerType);
I would suggest that it's the newline from the last time your program ran that code (you say it's the first time but it's unclear as to whether you mean the first time into that loop ever (since program started) or first time entering that loop but you've been through it before on this program run.
When you enter LENTER the first time, your code will pull out the L but not the ENTER. If you then call that code again, it will get the ENTER key.
You should either understand and allow for what's actually entered or use a safe and sound input function like this one.
You should always check the return value from scanf(); it tells you how many of the conversions succeeded. In this context, if you don't get back 1, you have a problem. The first time around the loop, scanf() reads a character - but not an ell (l or L) because you say you get an error message. The next iteration attempts to read the newline or whatever else follows the previous erroneous character, and the newline is certainly not an ell, and the other characters quite likely aren't an ell either, thus producing at least one more error message. You would get an error printed for each non-ell character.
Generally, if you use scanf(), it is fairly hard to recover from errors. You're likely to be better off reading a line into a buffer (character array) and use sscanf() to parse it.
You should just need to add a space before %c. I am unsure why it works, but it does. This also happens with other data types.
Replace your scanf statement with scanf(" %c",&computerType);

Resources