0

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.

regreading
  • 63
  • 1
  • 6

4 Answers4

2

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

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thanks for the advice. I will try to implement what you have said into my code. You are correct I haven't covered arrays and structures as yet. With regards to placing a 'break' after the 'default:' label I thought the idea of not having the 'break' was that it went back to the top of the 'switch'? – regreading Nov 26 '12 at 21:49
  • If you've not covered arrays and structures yet, treat my comments as a foretaste of things to come. Regarding `break`, the `switch()` statement is not a loop, so it never goes back to the top of the switch. In a `switch`, `break` stops execution of the current case and jumps to the statement after the end of the `switch`, just like `break` in a loop stops execution of the loop and jumps to the statement after the end of the loop. (You can't have a `continue` statement in a `switch` unless there's a loop surrounding the `switch` — and then the `continue` continues the loop, not the `switch`!) – Jonathan Leffler Nov 26 '12 at 21:52
  • Thanks again John for the input. Would you please be able to elaborate on 'printing the erroneous input as part of the error message', is this something that I need to setup in the compiler? With the testing of each scanf call. Do I need to place this code: `if (scanf("%c", &marker) != 1)` `...EOF or other problem...` after each scanf call or just at the start of main() to check each one? – regreading Nov 27 '12 at 10:29
  • Your code has `default: printf("You did not choose a correct option\n");`, but it could have been `default: printf("You chose '%c' but that is not a recognized option\n", choice); break;`, and when you saw the single quote at the end of one line and another at the start of the next, you'd know you were seeing a newline character. – Jonathan Leffler Nov 27 '12 at 14:53
2

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.

David Given
  • 13,277
  • 9
  • 76
  • 123
0

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

joze
  • 66
  • 5
  • Also your printf("Would you like to make another conversion? y/n\n"); will not be executed if you choose any of the cases because you are breaking the loop. – joze Nov 26 '12 at 21:39
0

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;
}
perreal
  • 94,503
  • 21
  • 155
  • 181