integer input in switch case and user enter strings - c

I am writing code for my switch case. I am taking input from the user and printing the respective day based on the input. But my question is, if user is giving string input it is printing case 0 statement as output. Can anyone please correct this program?
#include<stdio.h>
int main(void){
int days;
scanf("%d",&days);
switch(days){
case 0: printf("Mon");break;
case 1: printf("Tue");break;
case 2: printf("Wed");break;
case 3: printf("Thu");break;
case 4: printf("Fri");break;
case 5: printf("Sat");break;
case 6: printf("Sun");break;
default: printf("Plz enter a valid day(0-6) :( ");
}
return 0;
}
My output printing is :
>>a.out
Naveen
mon

scanf won't modify your int if the format specifier isn't found in the input.
In this case, you're switching on an uninitialized int. Doing anything with uninitialized POD types is generally A Bad Thing™, so you should take care that your value is initialized properly.
In this case, since you want it to trigger the default branch, initializing as int days = -1; (or as anything not in [0,6]) should do the trick.
PS: Note that scanf also returns an int telling you how many arguments it successfully found. This means you can check if scanf returned either 0 or EOF and handle this case separately - for example a more descriptive error message.

loop until days was properly scanned
while(scanf("%d",&days) != 1) getc(stdin);

Related

Ignoring scanf value in while loop

I'm learning C and I face a problem running this loop. I've wrote a while loop to prompt the user to key in the package and the quantity of it. I try to validate the input for the quantity to check is it integer or not (when a user key in a character it will prompt the user to key in again)
For the first run, everything is fine.
But when the loop runs a second time and so on, I try to key in a character for the quantity of the package, the message won't pop up to tell the user to key in again.
The value of the scanf is ignored and the value of tempQtty is equal to the previous quantity that the user keyed in.
Is there any way to fix this, or is there another way to validate the user input is integer?
Sorry for my broken English :")[input, expected input and actual input][1]
while(skip != 'x')
{
printf("\n\n%27sPACKAGE A/B/C/D ( x = skip ) : ", "");
rewind(stdin);
package = getchar();
switch (package)
{
case'x':case'X': skip = tolower(package); break;
case'A':case'a':case'B':case'b': case'C':case'c':case'D':case'd':
printf("%27sQUANTITY%21s: ", "", "");
rewind(stdin);
scanf("%d", &tempQtty); //here's the problem
while (tempQtty < 0)
{
printf("%27s(PLEASE KEY IN A CORRECT VALUE!)\n", "");
printf("%27sQUANTITY%21s: ", "", "");
rewind(stdin);
scanf("%d", &tempQtty);
}
switch (package)
{
case 'A':case 'a': qttyA = tempQtty; totalQttyA += tempQtty; break;
case 'B':case 'b': qttyB = tempQtty; totalQttyB += tempQtty; break;
case 'C':case 'c': qttyC = tempQtty; totalQttyC += tempQtty; break;
case 'D':case 'd': qttyD = tempQtty; totalQttyD += tempQtty; break;
}
break;
default:
printf("%27s(NO SUCH PACKAGE! PLEASE KEY IN AGAIN!)\n", "");
}
}
printf("\nA = %d", qttyA);
printf("\nB = %d", qttyB);
[1]: https://i.stack.imgur.com/hBD82.png
First of all, and as I mentioned in a comment, this is a surprisingly complicated problem, and you are not alone in facing it. It's not a problem with you, or with the C language; it's basically just a problem with the scanf function itself.
Partial answer:
(1) Everywhere you have things like
scanf("%d", &tempQtty);
while (tempQtty < 0)
...
that's wrong. If you ask for integer input using %d, and if the user types something non-numeric, what scanf does not do is fill in tempQtty as -1. What it does do is return a value saying it couldn't convert what you (the programmer) asked. So you want to change this to something more like
while (scanf("%d", &tempQtty) != 1)
...
(2) If the user does not type the integer you requested, and if scanf returns 0 to tell you so, there's a problem: the non-numeric input the user typed is probably still sitting on the input stream. It looks like you may have realized this, and that you're trying to get rid of the unread input by calling rewind(stdin). But that won't work; that's not the way to do it.
What you want to do is write a little "helper" function like this:
void flush_unread_input()
{
int c;
do {
c = getchar();
} while(c != EOF && c != '\n');
}
Then, wherever you've detected an error (that is, wherever scanf has returned something other than 1), instead of calling rewind(stdin), just call flush_unread_input().
A few more points:
You may have to experiment where to call flush_unread_input and where not to. You can't just blindly sprinkle it everywhere, because it basically reads and discards the rest of the line, but that it means it can also read and discard an entire line, which might sometimes be a line that you actually wanted.
There are many ways to write a flush_unread_input function. I've shown one way that should be easy to understand, but you'll often see something more compact like while((c = getchar()) != EOF && c != '\n');. (Also I haven't tested the version I've shown here.)
My answer might have suggested that scanf returns 1 if it succeeds and 0 if it fails, but it's more complicated than that. scanf actually returns the number of items successfully converted and stored, which might be more than 1 if you have a format specifier with multiple % signs in it.
There's much more to say on this topic, but I don't have time to write a longer answer this morning.
I try to key in a character for the quantity of the package, the message won't pop up to tell the user to key in again.
The value of the scanf is ignored and the value of tempQtty is equal to the previous quantity that the user keyed in.
Is there any way to fix this, or is there another way to validate the user input is integer?
There is no base for your assumption that tempQtty would receive a negative value if you entered z. In fact, the C standard mandates that tempQtty is not affected if the conversion fails. The way to fix this is to not ignore the scanf return value, which tells whether input was valid.

My program topped working when I enter a number

I used to compile in VSCode with mingw-64 in Windows 10.
Please rectify my errors.
[1]: https://i.stack.imgur.com/j5CKZ.jpg [
#include <stdio.h>
int main() {
float rupees,result;
int choice;
printf("Enter 1 for USD\n");
printf("Enter 2 for EUR\n");
printf("Enter 3 for AUS\n");
printf("Enter 4 for UAE Dirham\n");
printf("Enter amount in Rupees\n");
scanf("%d",&rupees);
printf("Enter your choice\n");
scanf("%d",choice);
switch (choice)
{
case 1:
result=rupees*73.04 ;
break;
printf("%f Amount equal to %f USD\n",rupees,result);
case 2:
result=rupees*88.82 ;
printf("%f Amount equal to %f EUR\n",rupees,result);
break;
case 3:
result=rupees*56.36 ;
printf("%f Amount equal to %f AUS\n",rupees,result);
break;
case 4:
result=rupees*19.89 ;
printf("%f Amount equal to %f Dirham\n",rupees,result);
break;
default:
printf("Enter correct choice");
break;
}
return 0;
}
Firstly, Let me try to review your thought process before getting onto the code.
You declared and read variables to store the value in rupees, the user choice and the conversion result.
You wrote a switch case to activate a particular block of code based on user choice and in a default case, you wrote a "printf" statement to convey to the user that the choice was invalid.
However, the program is tainted and below are the reasons why:
The Logic is invalid, as per your program, 1 rupee must be equivalent to 73.04 USD, 88.82 EUR,56.36 AUS and 19.89 Dirham accordingly which is a falsity. The vice versa is true! 1 USD=73.04 Rupees and so on. Quick Fix would be to replace "*" operator with "/" operator at every switch case.
You cannot actually read a fractional value for rupees using the above code as the placeholder for a float data-type in C is "%f" and not "%d".
In the line where you wanted to read the user choice, you did not use the Address-Of Operator(&) i.e you did not give the address location into which the value is to be read, you gave the identifier itself.
In the first switch case, you placed the break statement before the "printf" statement. Which would mean that you would break the control flow even before you would print something on your console.
In a default case, you asked the user to enter a valid choice but the problem is that the control terminates thereafter leaving no scope to re-enter the choice. A quick fix to this is to put the switch case within a loop or alternatively, just display an error message stating "Invalid Choice". (Note that such issues are called semantic errors where the structure of the program is valid as per the language rules but the overall logic and flow of the application is inconsistent).
Miscellaneously, there are some message-format errors that could be made better.
Ex: You don't have to ask to enter his/her choice given that you prior mentioned "Enter 1 for USD.....2 for EUR.....". Also, you could probably replace Amount with Rupees while printing the end-result to make the output more elegant and clear.
Last but not the least, the flow of your code is not so appropriate. Initially you display the entire catalogue to the user to choose from and then you ask the user to enter the value in rupees(you could ask the user to enter the rupee value irrespective of his conversion scale). By slightly rearranging the code, the usability of your script increases.
Want to look at Rectified Code? Here you go!
Rectified Code:
#include <stdio.h>
int main()
{
float rupees,result;
int choice;
printf("Enter amount in Rupees\n");
scanf("%f",&rupees);
printf("Enter 1 for USD\n");
printf("Enter 2 for EUR\n");
printf("Enter 3 for AUS\n");
printf("Enter 4 for UAE Dirham\n");
scanf("%d",&choice);
switch (choice)
{
case 1:
result=rupees/73.04 ;
printf("%f Rupees is equivalent to %f USD\n",rupees,result);
break;
case 2:
result=rupees/88.82 ;
printf("%f Rupees is equivalent to %f EUR\n",rupees,result);
break;
case 3:
result=rupees/56.36 ;
printf("%f Rupees is equivalent to %f AUS\n",rupees,result);
break;
case 4:
result=rupees/19.89 ;
printf("%f Rupees is equivalent to %f Dirham\n",rupees,result);
break;
default:
printf("That was an Invalid Choice!");
break;
}
return 0;
}

Infinite loop, program ignoring lines

I need a little help im sort of new to C programming. I'm having some trouble, for some reason the program won't read the line that im trying to take choice as input from the user which is causing me to have infinite loop neither the menu is printing.
Also stuff like this happen with me a lot of the time like when I copy my friends' code how it is it dosent run any loops even my teacher doesn't know why so if you can help in this too please do .
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
int main (){
int choice ,score=0;
char name [30];
puts("Welcome to our game , Have fun !\n ");
puts("Enter your name");
scanf("%c",name);
printf("1-Start new game\t2-show recorde\n3-Help \t4-Show score\n5-Reset score \t6-Quit game");
scanf("%d",choice);
while(choice!=6){
switch(choice){
case 1 :
break;
case 2 :
break;
case 3 :
break;
case 4 :
break;
default :
puts("\n invalid input ");
break;
}//end switch
}//end while loop
}//end main
Your usage of scanfis wrong.
scanf("%c",name);
Format specifier %c is not suitable to read a string. It only reads 1 single character.
The other members of name array will be untouched and as local variables are not initialized to 0, that is not a valid nul-terminated string.
As soon as you try to print name or perform any string operations with it, this will result in undefined behaviour.
As a result, whatever you type after first character, stays in input buffer and is fetched during next read attempt.
To fix this, use format specifier %s.
When you now try to read the choice as an integer
scanf("%d",choice);
the input buffer contains remaining letters and also \n from first call, which does not match any number.
After the call choice is not modified and still contains its indetermined content (You don't initialize it)
You should always check return value of scanf to see how many parameters were read. It would show you that it didn't find any value.
Besides that, you also provide invalid parameter. You must provide the address of an integer. This is undefined behaviour.
This is the reason why you always enable warnings in your compiler. It should warn you about this error.
To fix this, use
int ret = scanf(" %d", &choice);
if (ret != 1)
// error handling, try again, etc.
Note the space in the format string to skip whitespace including \n.
Finally, you enter your loop with a "random" value in choice and never try to read again. How would you expect to leave the loop without this?
If your teacher really doesn't know why, the missing indentation might be a reason.

Getting zero answer while using DO-WHILE loop

I tried to calculate the tot(total fee) in the do-while loop, but all I get is tot=0.00?! Why is this happening? And after that I get a message: it said the variable fee is not being initialised?
#include<stdio.h>
int main(void)
{
int cofno;
float tot=0.0;
float fee;
char option;
do{
printf("Enter cofno: ");
scanf("%d",&cofno);
if(cofno>0)
{
printf("Key in (h/c): ");
scanf("%c",&option);
getchar();
switch(option)
{case 'h':fee=cofno*1.80;
break;
case 'c': fee=cofno*2.50;
break;
}
tot=tot+fee;
//the program will repeat until the user key in negative value or zero
}
}while(cofno>0);
printf("\ntot=RM%.2f \n\n",tot);
return 0;
}
scanf(" %c",&option); This will solve the problem for you. The reason the ' ' is provided in the scanf so that it can consume the white space characters.
What happened earlier was that your character input got the \n from previous input.
To check the thing that it inputted \n try outputting the option like this
printf("[%c]",option); you will see output
[
]
Also the break statement you provide is breaking for the case staement. Not the while loop. You have infinite loop now. You can solve this with a added condition.
...
tot=tot+fee;
if(option == 'c' || option =='h')
break;
...
Even more simply, you could have changed the while condition overall and make it like this
while(cofno<=0);
this conforms to your idea the program will repeat until the user key in negative value or zero more suitably.

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).

Resources