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;
}
Related
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.
I have a very simple program that simply asks for ones weight and converts it to the value of platinum. I am new to C so the mistake could be anywhere. But when I use scanf, it asks for input at the very beginning rather than following the sequence of code:
code:
#include <stdio.h>
int main(void)
{
float weight;
float value;
printf("Are you worth your weight in platinum?\n");
printf("Let's check it out.\n");
printf("Please enter your weight in pounds: ");
scanf("%f", &weight);
printf("%.2f\n", weight);
value = 1700.0 * weight * 14.5833;
printf("Your weight in platinum is worth $%.2f.\n", value);
printf("You are easily worth that! If platinum prices drop,\n");
printf("eat more to maintain your value.\n");
return 0;
}
Output:
123
Are you worth your weight in platinum?
Let's check it out.
Please enter your weight in pounds: 123.00
Your weight in platinum is worth $3049368.00.
You are easily worth that! If platinum prices drop,
eat more to maintain your value.
If you notice in the output the user has to enter input before the first line prints. Why is this?
I tried your program and it worked as you want for me. You might try flushing the stdout buffer before you scan for input.
fflush(stdout); // right before your scanf line.
The OS is free to postpone writing output buffers for efficiency. So, for instance, you write to the disk in blocks instead of one character at a time. fflush forces the buffer to be written. It's "endl" in C++ and fflush is the straight c version. I'm not certain that's what you're seeing.
Can someone tell me why the gets in the case 1 is being completely ignored?
// #define M 50 is at the top
char product[M] = {0};
int choice = -1;
printf("Command: ");
scanf("%d", &choice);
switch(choice){
case 0:
break;
case 1:
printf("Product: ");
gets(product);
insert_product(warehouse, price, product);
break;
case 2:
// print_all();
break;
default:
printf("Scelta non valida\n");
break;
}
The gets might be skipped by an ending char from a previous input. Try adding a getchar() before the gets to see if that is the problem.
The problem is that the newline you input when reading choice:
scanf("%d", &choice);
is being taken as input for the next gets(product); and so it reads an empty string. Try to add an artificial getchar after scanf to absorb the newline:
scanf("%d", &choice);
getchar();
Should work now.
First, the important bit...
NEVER NEVER NEVER NEVER NEVER use gets. Ever. Even for practice code. Even if someone else tells you to. It's like the movie Highlander II; it's just best to pretend it never existed. It was deprecated in C99 and has been completely removed from C2011. Don't use it. Don't try to justify using it by claiming this is just practice code. If someone else demands you use it, push back and tell them you'll use fgets instead. If they give you grief, send them to me and I'll straighten them out. With a baseball bat if necessary.
Okay, now that I've cleared that up...
gets is picking up the trailing newline from the input you entered for the previous scanf call. You have several choices:
Use another scanf call instead of gets, this time with the %s conversion specifier (along with a field width); unlike gets, it will skip over any leading newline characters;
Call getchar() (or equivalent) to consume the newline before calling gets;
Instead of reading choice as an integer using scanf, read it as text using fgets and convert it to an integer using strtol.
Option 3 is the best IMO. Mixing calls to scanf/fscanf and fgets is usually a recipe for heartburn, precisely because of the trailing newline issue. Better to stick with one or the other for everything. The advantage of using fgets to read everything as text is that it makes input validation easier, especially for numeric input.
// #define M 50 is at the top
char product[M] = {0};
int choice = -1;
printf("Command: ");
scanf("%d", &choice);
switch(choice){
case 0:
break;
case 1:
printf("Product: ");
getchar();
gets(product);
insert_product(warehouse, price, product);
break;
case 2:
// print_all();
break;
default:
printf("Scelta non valida\n");
break;
}
Use fflush(stdin) before using gets as input as the gets is taking newline character as input after the use of scanf("%d", &choice);
fflush(stdin) clears the output buffer and moves the buffered data to console.
#include <stdio.h>
#define TRUE 1
#define FALSE 0
typedef struct contact {
char firstname [40];
char lastname [40];
char address [100];
char phone[10];
}contact;
int main ()
{ FILE *pFile;
contact entry = {""};
int choice, firstname_flag = TRUE, lastname_flag = TRUE, address_flag = TRUE, phone_flag = TRUE;
pFile = fopen("C:\\contacts.txt", "a+");
if(!pFile){
printf("File could not be open.");
return 1;
}
do{
printf("Choose a selection:\n\n");
printf("1. First name:\n");
printf("2. Last name:\n");
printf("3. Address:\n");
printf("4. Phone number:\n\n");
scanf( "%d", &choice);
}while((choice < 1 || choice > 4));
switch (choice){
case 1:
firstname_flag = FALSE;
printf("Please enter first name: \n");
scanf("%s", &entry.firstname);
break;
case 2:
lastname_flag = FALSE;
printf("Please enter last name: \n");
scanf("%s", &entry.lastname);
break;
case 3:
address_flag = FALSE;
printf("Please enter address: \n");
scanf("%s", &entry.address);
break;
case 4:
phone_flag = FALSE;
printf("Please enter phone number: \n");
scanf("%s", &entry.phone);
break;
default:
break;
}
printf("\nYou will now be asked to enter the other items. \n\n");
if(firstname_flag){
printf("Please enter first name: \n");
scanf("%s", &entry.firstname);
}
if(lastname_flag){
printf("Please enter last name: \n");
scanf("%s", &entry.lastname);
}
if(address_flag){
printf("Please enter address: \n");
scanf("%s", &entry.address);
}
if(phone_flag){
printf("Please enter phone number: \n");
scanf("%s", &entry.phone);
}
fwrite (here)
fclose(pFile);
getchar();
return 0;
}
Here is the code I have so far. First of all is there anything blatant that pokes out as being invalid or wrong practice etc? 2nd I want to write first name,last name,address, and Phone # to a file. I'm unsure if I need to fwrite "entry" or not. Also, I have noticed when selecting Address first that It's almost like the buffer isn't empty and what I put after the space i.e. 123 Park , Park would be used as the first name wrongly and the next entry I would put in would be Last name. Any suggestions on the code usage and anything at all would be greatly appreciated. Thanks again
1
N° 1. In response to: “invalid or wrong practice.” Some of these are more matters of taste…
If you ‘must’ use scanf, don't point it at a string buffer with %s: specify the length of the buffer with something like %32s to reduce (but perhaps not eliminate) someone from just typing too much and crashing your program (or worse…) — But, Read N°3 below for more on this question…
If you're developing something open-source, or for personal use, GNU readline is a very nice option (but it is GPL not LGPL, so it can't be used in "non-libre" works…)
Your structure initializer doesn't cover all the elements. You could use {"", "", "", ""}
Did you mean fwrite(entry)?
If you're checking for missing fields before writing the file, you might want to leave aside the bank of flags, and instead loop on the "invalid condition."
while ('\0' == entry.firstname[0]) {
printf("Please enter first name: \n");
scanf("%40[^\r\n\0\04]", &entry.firstname[0]);
}
No need to open the file so early, and leave it open while waiting for user interaction. Opening the file in a+ mode is reasonably "dangerous" (see man lockf for the skinny), because someone else might try to write to the file while you are; leaving it open in this mode for a long time increases the risk.
You should probably use strerror(errno) to provide the user the details of a failing system call, such as when checking the return codes of fopen, fwrite, and fclose. Due to the way buffered I/O works, fclose could even be reporting a problem that occurred with fwrite under some circumstances.
Print your error messages to stderr using fprintf (stderr,…); instead of the output stream …
If you are going to use flags to indicate that the data is "valid" (or, at least, that it may be), you should probably do so after the user tries to enter it, instead of before.
Functions are your friends … I would probably break up something like this by using a function to collect the user's input (something like prompt_for_field ("first name", &entry.firstname);), a function to check for missing records and prompt for them, and a function to write the record to the file, at least …
It's usually considered good form to exit from main, rather than return, for example by using exit(EXIT_SUCCESS)/exit(EXIT_FAILURE). I believe the idea is to support esoteric and possible extinct systems who might treat some value(s) other than 0 as a successful status code (VMS, perhaps?), but regardless, it's easier to read :-)
2
And N°2, yes, you can fwrite(entry), and as long as you never change the definition of struct contact, you should be able to read it back in all right. Over time, you'll probably want to switch to a more "plain text" type format (#include <json-xml-init-religious-war>), but in this small example, there isn't a pressing need to introduce such complexity.
3
Finally, N°3, you should probably use &entry.address[0] to get the address of the start of a char[] but most significantly: scanf %s does not read a string. It looks like printf %s, but it's not…
s Matches a sequence of non-white-space characters; the next
pointer must be a pointer to character
array that is long enough to hold the input sequence and the terminating null character ('\0'),
which is added automatically. The input string stops at white space or at the maximum field width,
whichever occurs first.
See that "non-white-space?" That's what's got you. The rest of your input (after the first whitespace) is left in the keyboard buffer, waiting for another scanf to read it. A good('ish) way to read from the terminal and accept whitespace is %40[^\r\n\0\04], replacing 40 with the size of your string buffer (char[]). That means, accept (up to) 40 characters, as long as they are none of: carriage return, new line, the null byte, or end-of-file code (^D).
All-in-all, you look to be on the right track, though. Good luck :-)
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);