Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I am new to C, trying to make a simple banking system for an assignment in my C programming class. I decided to use a switch statement inside a while loop. The program runs without error messages in Visual studio, but behaves very oddly. Here is the assignment:
"Make a simple banking system. Start with the program asking for a starting balance for the savings account. From there it needs to ask which account should be modified: Savings, Checking, Loans, and Credit.
Checking needs to be set 250 dollars. This is the money available in the checking account to pay bills.
Loans needs to be set at 9000 dollars. This is the money owed on a loan. This is a bill.
Credit needs to be set at 500 dollars. This is the credit card balance owed. This is a bill.
The program should (after asking which account to modify) allow the user to move money within that section.
Sample: You are in Checking, do you want to pay a loan payment, credit payment, move money to savings from checking or return to the main menu.
Checking is your central point. You can move money any place from checking but you cannot pay a bill directly from Savings."
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
int savings;
int checking = 250;
int loans = 9000;
int credit = 500;
int moneyMover;
char choice = 'y';
int service;
int service2;
printf("Welcome to C-Bank!\n\n");
printf("What is the current balance for savings? ");
scanf("%d\n", &savings);
printf("Thank you. \n\n");
while (choice == 'y')
{
printf("Which account or bill would you like to manage? \nPress '1' for Savings, '2' for Checking, '3' for Loans, or '4' for credit. Else, press any other key to exit.");
scanf("%d\n\n", &service);
switch (service)
{
case 1:
printf("Welcome to your savings account. Your current balance is %d. \n", savings);
printf("Please choose from the following: \nWould you like to (1) make a deposit, (2) make a transfer to checking, or (3) return to the main menu.\n");
scanf("%d", &service2);
if (service2 == 1)
{
printf("Enter deposit amount: \n");
scanf("%d \n", &moneyMover);
savings = savings + moneyMover;
printf("Your new balance is: %d\n", savings);
}
break;
default:
break;
}
printf("Would you like to choose another service? If so, press y, else press any other key.\n");
scanf(" %c", &choice);
}
printf("Thank you for using C-Bank. Come back soon!");
system("PAUSE");
return 0;
}
The program runs without error messages but acts weird. When you enter the initial savings amount, nothing happens until you press 'y', at which point it
apparently jumps inside the while loop, skips the switch statement, and waits for the you to pick again. Or, if you enter another number, it will quickly display the rest of the printf statements and end the program. Other weird things happen, too many to name really.
One problem is e.g. this
scanf("%d\n\n", &service);
The trailing newlines makes scanf read and discard any trailing white-space (space, newline, tab, etc.). But the problem with that is that scanf needs to see a non white-space character before it knows the end of the spaces to read and discard.
Don't have any trailing white-space in a scanf format string, it will seldom work as intended.
On the other hand you do the correct thing when reading the character for choice, when you use a leading space to read and discard leading white-space (like the newline from the previous input).
Another problem is that you state that after the savings input, you give the input 'y'. But that's not what the next input operation is expecting, it's expecting an integer for the service.
If you don't give the input as an integer, then
scanf("%d", &service);
will fail, and return 0 (you should really check what scanf returns). It will also mean that the 'y' in the input will be left in the input buffer for your read of choice. This is why it seems to skip the switch.
The first thing you should do after fixing the trailing space and newline in the format string, is to give the correct input. Then you can start adding error checks to make sure the input is correct.
One common way to handle possible incorrect input is to read whole lines using fgets and then use the string-scan function sscanf to parse the line. That way the invalid input will not be in the input buffer messing things up.
Related
I really have no idea what I'm doing wrong with this. Every time I compile it, at the fourth user input, it just stops and shows the "processes returned" stuff.
#include <stdio.h>
#include <conio.h>
int main() {
char firstname[15], class, swordch0c1, swordch0c2;
int health, healthtot, armor, armortot;
printf("Hello there! Could I have your first name?\n>");
scanf("%s", firstname);
printf("\n---------------------The Legend of %s---------------------", firstname);
printf("\nPress Enter to continue.");
getch();
printf("\n\n\nYou are %s, a(n): \nA.Swordsman\nB.Assassin\nC.Archer\nD.Mage\n>", firstname);
scanf(" %c", &class);
/*swordsman story starts here*/
if (class=='a' || class=='A')
{
printf("\n\nThere you stand, at your boring everyday post.\nWhen you joined the army, you thought it would be more exciting than this.\nJust then, you see your general walking towards you.");
printf("\n\nYou quickly improve your posture. \"Soldier, I have an opportunity for you\"\nA.\"Really? What is it?\"\nB.\"I'm not interested\"\n>");
scanf(" %c", &swordch0c1);
if (swordch0c1=='b'||swordch0c1=='B')
{
printf("\n\"But... I didn't even tell you what it was. Okay, suit yourself\" You are DOOMED to a life of boredom.\n\n\n\n\n");
}
if (swordch0c1=='a'||swordch0c1=='A')
{
printf("\n\n\n\"Well, you see, there's this dragon. He's been causing big problems.\nHe's destroyed villages, harrassed the priests on the mountain,\n");
...
edit:It does allow me to put in the fourth input. after hitting enter, it shows the processes returned stuff.
This line is almost definitely the problem:
scanf(" %c", &swordch0c1);
You are asking to read a single character, however to actually enter a character you have to type two characters: AEnter. This scanf call will read the 'A', and the next scanf call will read the '\n' (the Enter) key, which is still in the input buffer.
I suggest using fgets() for all user input. It's a lot easier to deal with that way, because it matches the way users actually enter input (line of text plus Enter).
Can anyone tell me why my code works fine until I get to the final scant, where I ask the user if they'd like to play again? For some reason, the program seems to ignore this line of code. Please be gentle as I'm new to programming and trying to teach myself Objective-C. This program is typical for noobs, where I generate a random number, ask the user to guess, then ask if they'd like to play again. Thank you.
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
int randomNumber = arc4random_uniform(100); // This is a random number generator that gens a num betw 0 and 100
int userNumber; // This is the number that the user picks intially
int attempts = 0; // This is the number of attempts the user makes during each game
int games = 0; // This is the number of games the user has played
char play = 'n'; // This is whether the user wants to play again, intially set to 'y'
scanf("%c", &play);
while (play == 'y') {
NSLog(#"Random number is: %d", randomNumber);
NSLog(#"Enter a number between 0 and 100");
scanf("%d", &userNumber);
games++; // Increment the number of games the user has played to 1
if (userNumber == randomNumber) {
attempts++;
NSLog(#"Congratulations. You guessed correctly!");
}
attempts++;
while (userNumber != randomNumber) {
if (userNumber < randomNumber) { // Guess is too low
attempts++; // attempt is incremented
NSLog(#"Too low. Try again!"); // User tries again
scanf("%d", &userNumber);
}
if (userNumber > randomNumber) { // Guess is too high
attempts++; // attempt is incremented
NSLog(#"Too high. Try again!"); // User tries again
scanf("%d", &userNumber);
}
}
NSLog(#"Congratulations. You guessed correctly!");
NSLog(#"It took you %d attempts to guess correctly", attempts);
NSLog(#"Do you want to play again?");
scanf("%c", &play); // --------- Here is where things to wrong ---------
} // while play is yes
} // autoreleasepool
return 0;
} // main
Converting comments into answer:
Probably, the final scanf() reads a newline and continues (the numbers don't read the newline). Maybe put a blank before the %c:
scanf(" %c", &play);
Check the return value from scanf(), and maybe even check which character was read.
Riposte:
That space before the %c did the trick. How does anyone ever learn things like that? I think it was reading the \n char rather than what I wanted it to read, which was either 'y' or 'n'. For my understanding, the %d integer doesn't read in the newline, but the %c does? Is that correct? And the way to prevent this is to use a space? I just don't get how I would ever know to do that.
And the response:
By reading the manual page for scanf() very carefully, many times over, or by bitter experience (or by answering lots of questions on SO about it). The scanf() family of functions are extremely powerful and extremely difficult to use accurately. I generally recommend using fgets() to read lines of input:
char line[4096];
if (fgets(line, sizeof(line), stdin) != 0)
{
...use line...
}
combined with sscanf() to parse the data on the line. It generally leads to fewer surprises of the sort you just ran into. You should always check that scanf() made as many conversions as you expected.
The role of white space in scanf()-family format strings is intricate. Most of the conversion specifiers skip leading white space (including newlines) automatically, so a format string "%d%d" will read to integer values where the first may be preceded by an arbitrary amount of white space, and the second may also be preceded by an arbitrary amount of white space. The conversion will stop at the first character that could not be part of the second integer (unless there was an error earlier). If you type 8 and newline as input, then the conversion stops on the newline (\n) and leaves if for the next input to read.
The numeric conversions and the string conversion %s all skip leading white space. The single-character format (%c) and the scan set %[a-z] do not skip leading white space.
When a white space character appears in the format, as in "%d %c", then it represents an arbitrary amount of white space in the data, including zero. Thus, in each of the following lines, the variable receiving the %c format will get Z each time:
123Z
123 Z
123 Z
123
Z
(The last two lines are read together for the last input.)
I've put together a simple currency converter program from code I've edited from the web and a switch statement that I've put in. When I ran the program in Visual Studio:
printf("Would you like to make another conversion? y/n\n");
fflush(stdin);
scanf("%c",&marker);
would wait for input and then either go back to the start of the while statement or exit the console window. Now if I run this is Xcode on a mac "Would you like to make another conversion..." is printed but doesn't wait for an input, it just goes straight back to the while loop.
Am I putting the printf("Would you like...etc section in the right place? Or is there a better way of getting loops to run again after they take input from a user? Do I need to do anything with a 'bool' statement. We've not got that far in class yet.
Full code is shown below:
#include <stdio.h>
int main ()
{
int choice;
float money;
float total;
char marker='y';
printf("\n\nCURRENCY CONVERSION\n");
printf("***Rates correct as of 26 NOV 12***\n");
printf("------------------------------------\n");
printf("1. Australian Dollar (AUD) 1.533=1 GBP\n");
printf("2. Euro (EUR) 1.235=1 GBP\n");
printf("3. Indian Rupee (INR) 89.494=1 GBP\n");
printf("4. Japanese Yen (JPY) 131.473=1 GBP\n");
printf("5. US Dollar (USD) 1.602=1 GBP\n");
printf("Enter the number for the currency to convert...");
while(marker!='n')
{
printf("\n\nWhat would you like to convert your money to? (1-5): ");
scanf("%d",&choice);
printf("\n\nHow much money do you want to convert? (GBP): ");
scanf("%f",&money);
switch(choice) {
case 1:
total = money * 1.533;
printf("\n\nYou will have %.2f Australian Dollars \n\n", total);
break;
case 2:
total = money * 1.235;
printf("\n\nYou will have %.2f Euros \n\n", total);
break;
case 3:
total = money * 89.494;
printf("\n\nYou will have %.2f Indian Rupees \n\n",total);
break;
case 4:
total = money * 131.473;
printf("\n\nYou will have %.2f Japanese Yen \n\n", total);
break;
case 5:
total = money * 1.602;
printf("\n\nYou will have %.2f US Dollars \n\n", total);
break;
default:
printf("You did not choose a correct option\n");
}
printf("Would you like to make another conversion? y/n\n");
fflush(stdin);
scanf("%c",&marker);
}
return 0;
}
Many thanks for any comments of input received.
Your problem is likely that the scanf("%c", &marker); is reading a newline left over by a previous input.
Note that fflush(stdin) invokes undefined behaviour. The C standard says of fflush():
If stream points to an output stream or an update stream in which the most recent operation was not input, fflush() shall cause any unwritten data for that stream to be written to the file.
If that operation clears the input queue in Visual Studio or on Windows, then you've just learned the hard way that extensions to the standard on one platform do not always work on other platforms. In particular, fflush(stdin) does not clear queued characters waiting to be read.
Note that you could have helped your debugging efforts by printing the erroneous input as part of the error message.
You should also be testing each scanf() call:
if (scanf("%c", &marker) != 1)
...EOF or other problem...
if (scanf("%f", money) != 1)
...EOF or maybe a letter instead of a number...
Stylistically, it is best to include a break; after the code after the default: label in the switch.
Also, you should really avoid repeating the conversion rates in the strings that are printed in the menu and in the case statements. Similarly with currency names. Maybe you haven't covered arrays and structures yet, but in the long term, that is a recipe for problems (if only wasted recompilations because the rates have changed; but allowing them to be read from a file, or a web site, is another major exercise).
This isn't anything to do with the loop. It's about your calls to scanf.
What's happening is that stdin is buffered. scanf will only cause the program to halt and wait for input if there isn't enough pending data in the buffer. When you do this:
scanf("%f", &f);
...then the program will halt until you type a line of text. Say you type:
1\n
The 1 is read into the variable f... but the newline remains in the buffer.
This means that your call to:
scanf("%c", &c);
...will return immediately, because there's enough data in the buffer to complete the call. The program will only stop and wait for more data when it runs out of buffer.
Windows doesn't do buffering like this, which is why you weren't observing the behaviour there.
There are about a million solutions to this, ranging from changing the way stdin is buffered to using a text-based UI library like ncurses. Given that by default stdin is line-buffered --- data is read a line at a time, terminated with newlines --- the simplest is simply to make sure that each call to scanf() consumes a complete line of text. That means that the buffer will be empty each time you call scanf. For example:
scanf("%f\n", &f);
...and:
scanf("%c\n", &c);
BE AWARE that if the user enters input that doesn't match this format string, such as 1 2 for the first or ab for the second, scanf will leave the variable uninitialised. So you absolutely have to check the return parameter.
if (scanf("%f\n", &f") != 1)
error("Oh noes!");
Otherwise your program will go horribly, horribly wrong.
As an aside: you haven't done it in this program, but just so you're aware, scanf("%s") should not be used. For anything.
You can do an infinite loop(eg. while(true){} ), and in your switch statement have a case for choice 'n' that breaks the loop (eg. break;)
Also, case 1 , case 2, etc... I would scan them as chars instead of integers. (eg. case '1':, case '2':, etc...)
I am not sure what I just said is going to work but that is my initial thought. Hope it helps :)
If you don't want to deal with flushing buffers, you can define a char buffer[1024] and use fgets with sscanf:
printf("\n\nWhat would you like to convert your money to? (1-5): ");
fgets(buffer, sizeof(buffer), stdin);
sscanf(buffer, "%d", &choice);
and the while loop can include your menu:
while (1) {
printf("\n\nCURRENCY CONVERSION\n");
printf("***Rates correct as of 26 NOV 12***\n");
/* ... */
printf("Would you like to make another conversion? y/n\n");
fgets(buffer, sizeof(buffer), stdin);
sscanf(buffer, "%c", &marker);
if (marker == '\n')
break;
}
I'd like to restrict the possible values that the following patient_id variable can have(for security reasons). The following is the code I currently have, but I'm pretty sure this is the wrong approach:
void addPatient(){
int patient_id;
printf("Enter ID between 10000 & 99999: ");
do{
scanf("%d", &patient_id);
}while((patient_id<10000)&&(patient_id>99999));
}
Unless you're writing a program for homework problem, and in particular if you're writing a program that an actual human user will interact with in any serious way, don't use scanf(). Operate on lines of input at a time. If you still want to use scanf()'s 'parsing', you can use sscanf() and like on a complete line of input after you have it. If you don't know how to get user input other than through scanf(), a simple way is to fgets() into a buffer, pick out the line, and prepare the buffer for the next fgets().
If you insist on using scanf(), I have a challenge for you: use it to accept two IDs on the same line, separated by a space. If the user enters only one ID and then hits enter, your program should complain about this to the user before requesting any more input.
your condition should be
while((patient_id < 10000) || (patient_id > 99999))
for this purpose
This code should restrict the value the patient_id should have.
This is not the most efficient way by far, but it if you want to use scanf(), then this can do the job.
#include <stdio.h>
int patient_id(void);
int main(void)
{
int id=0;
id = patient_id();
printf("\nThe patient_id is: %d\n", id);
}
int patient_id(void)
{
int p_id;
int ch;
printf("Enter ID between 10000 & 99999: ");
scanf("%d", &p_id);
while ((p_id <10000 || p_id >99999))
{
while((ch = getchar()) !='\n')
continue;
printf("Please enter a value between 10000 and 99999: ");
scanf("%d", &p_id);
}
return p_id;
}
(1) The check you're using is correct; .*scanf doesn't know integer range restrictions.
(2) As other posters noted, scanf() does not beget good user interfaces. Use readline() to get lines and sscanf to parse them and you'll be happier.
(3) You're restricting these values for security purposes? If this is actually a security issue, those should not be magic numbers but #defines or static consts or globals, so that if somebody changes those ranges without updating your code your security check isn't compromised.
You can not contol input values while accepting these values. You have to first accept values and then you can check condition whether the input accepted is correct or not. You can generate a warning for wrong inputs.
In above code if you want patient_id between 10000 to 99999, then your code is correct, but user will enter values till he/she enters a correct input.
eg.
1, 200124, 45, 9999, 321, 12001.
Here your prgm will stop accepting input as user enters 12001.
I'm just starting to learn the C programming language. I've written this simple piece of code that converts the USD to Euros. Only problem is that once the input of the USD is entered, the program just hangs. I'm using a while loop to ask the user whether or not they want to redo the operation, so my guess is that the syntax of the code is causing an eternal loop, but I'm not for sure.
Here's my code:
#include<stdio.h>
#define conv 0.787033 //conversion factor of USD to Euro found www.ex.com//
int main(void)
{
float USD, EURO;
char cont = 'y';
while (cont == 'Y' || cont == 'y')
{
printf("Please enter the amount of United States ");
printf("Dollars you wish to convert to Euros\n");
scanf("%f\n", &USD);
EURO = USD * conv;
printf("%.2f dollars is %.2f Euros\n", USD, EURO);
printf("Do you wish to convert another dollar amount (Y/N)?\n");
scanf("%c\n", &cont);
}
return(0);
}
remove the \n from your scanf
EDIT:
The above change should not work.
When reading input using scanf, the input is read after the return key is pressed but the newline generated by the return key is not consumed by scanf, which means the next time you read from standard input there will be a newline ready to be read.
One way to avoid is to use fgets to read the input as a string and then extract what you want using sscanf.
Another way to consume the newline would be to scanf("%c%*c",&cont);. The %*c will read the newline from the buffer and discard it.
C faq on problems with scanf
the \n inside scanf require the user to press another extra "ENTER" (Line Feed) in order to continue. Remove it or you press Enter a few time, it will continue
Avoid scanf()
It's best to use fgets(3) and then sscanf(3) because it is invariably the case that an interactive program knows when a line of input is expected.
So, the usual sane-scanf design pattern does make the program a bit more complex but will also make it more flexible and more predictable, try something like changing...
scanf(...)
to
char space[100];
fgets(space, sizeof space, stdin);
sscanf(space, " %f", &USD);