When I run my code twice (after a While/DoWhile),

2019-08-25 02:18发布

问题:

I'm new to programming and I was trying to make a calculator and I'm adding a while loop, so that if you want to repeat you just type "1" and the program repeats. The problem is if I repeat it the scanf()breaks and no longer allows me to type something into the command line. (I am using Visual C++ Windows Console Application)

I was trying to use fflush(stdin) to clear the keyboard buffer, this did not work either.

#include "pch.h"
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>


void main() {
    char v;
    int exit=1;
    while (exit == 1) {
        v = 0;
        //Read what type of calcualtion the user wants to do.
        printf("Type (+,-,*,/): ");
        fflush(stdin); 
        scanf("%c", &v);
        //system("cls");
        //show the user for 2 sec what he chose
        printf("Type you chose: %c", v);
        Sleep(2000);
        //system("cls");
        //here the calcualtion will take place. 
        switch (v) {
        case '+':
            printf("\nTBD +");
            break;
        //Here are some more cases that i have excluded.
        default:
            printf("Please only use '+,-,*,/' above\n");
            exit = 1;
            break;
        }
        printf("\n do you want to repeat (1==yes|0==no): ");
        scanf_s("%d", &v);
    }
}

The result when this program runs looks like this:

Type (+,-,*,/): +
Type you chose: +
TBD +
 do you want to repeat (1==yes|0==no): 1
Type (+,-,*,/): Type you chose:
Please only use '+,-,*,/' above

do you want to repeat (1==yes|0==no):

The result should look something like this:

Type (+,-,*,/): +
Type you chose: +
TBD +
 do you want to repeat (1==yes|0==no): 1
Type (+,-,*,/): +
Type you chose: +
TBD +
 do you want to repeat (1==yes|0==no): 1

回答1:

There's a lot of issues with your code. For one, you didn't need the windows header or needed to use scanf_s. Also fflush(stdin) results in undefined behavior. You should clear the input stream yourself. As an alternative to scanf, use fgets or fgetc and perform the conversion yourself. The other issue in your code is you're resetting the value of v at the beginning of the loop. And then you give a default value of 1 to x, but check while(x==1) You're also trying to run the code at least once regardless of the initial condition and do while loops are a better alternative to while in this case. Also just for naming convention if you're continuing the loop at exit == 1, that's misleading. If exit == 1, then you should terminate the loop. it's very convoluted and confusing code. Let me try to clean it up for you.

int main()  {
    //We only need a size of 3 , 1 for character, 1 for null terminator,1 for carriage return
    char v[32] = {0};
    int exit = 0;
    do{
        //Read what type of calcualtion the user wants to do.
        printf("Type (+,-,*,/): ");
        fgets(v, sizeof(v), stdin);
        //system("cls");
        //show the user for 2 sec what he chose
        printf("Type you chose: %c", *v);//dereference the pointer to the first character
        //system("cls");
        //here the calcualtion will take place.
        switch (*v) {//dereference the pointer to the first character
            case '+':
                printf("\nTBD +");
                break;
                //Here are some more cases that i have excluded.
            default:
                printf("Please only use '+,-,*,/' above\n");
        }
        printf("\n do you want to exit (1==yes|0==no): ");
        fgets(v, sizeof(v),stdin);
        exit = atoi(v);
    }while(exit != 1);
}

We give v a size of 32, although if we are only entering 1 character then a size of 3 is sufficient. Mainly because entering a single character with fgets will consume three bytes. But since we are taking an integer value at the end of the loop, we want to make sure there's enough room in the buffer. In case the user enters 123 for example, the buffer will still be fine and extra bytes will not remain in the stream.



回答2:

To make your original code work

  • remove the call to fflush(stdin) as fflush() is undefined for input streams.
  • change scanf("%c", &v); to scanf(" %c", &v); (mind the space before the conversion specifier %c) to make scanf() skip leading whitespace.
  • change scanf_s("%d", &v); to scanf_s("%d", &exit);. The compiler should have warned you about a type mismatch between conversion specifier %d and the argument &v (int* vs. char*). If it didn't you should increase the warning level of your compiler.

Possible implementation with error checking on input using scanf():

#include <stdio.h>

int main(void)
{
    char keep_running;
    do {
        double first_operand;
        while (printf("First operand: "), scanf("%lf%*[^\n]", &first_operand) != 1)
               fputs("Input error. :(\n\n", stderr);

        char op;  // operator  (not named "operator" in case a C++-compiler ever sees this file)
        while (printf("Operation: "),
               scanf(" %c%*[^\n]", &op) != 1 || (op != '+' && op != '-' && op != '*' && op != '/'))
        {
            fputs("Input error. :(\n\n", stderr);
        }

        double second_operand;
        while (printf("Second operand: "), scanf("%lf%*[^\n]", &second_operand) != 1)
               fputs("Input error. :(\n\n", stderr);

        switch (op) {
        case '+':
            printf("\n%f %c %f = %f\n\n", first_operand, op, second_operand, first_operand + second_operand);
            break;

        case '-':
            printf("\n%f %c %f = %f\n\n", first_operand, op, second_operand, first_operand - second_operand);
            break;

        case '*':
            printf("\n%f %c %f = %f\n\n", first_operand, op, second_operand, first_operand * second_operand);
            break;

        case '/':
            if(second_operand)
                printf("\n%f %c %f = %f\n\n", first_operand, op, second_operand, first_operand / second_operand);
            else fputs("\nDivision by zero is undefined. :(\n\n", stderr);
            break;
        }

        while (printf("Do you want to repeat (y/n)? "),
               scanf(" %c%*[^\n]", &keep_running) != 1 || (keep_running != 'n' && keep_running != 'y'))
        {
            fputs("Input error. :(\n\n", stderr);
        }

        puts("\n");

    } while (keep_running == 'y');
}
  • Please mind that the parameter list of functions that don't take any arguments should be void in C, hence int main(void).
  • scanf() returns the number of successful assignments. Check that return value and handle input errors. Never trust the user.
  • The conversion specifier %*[^\n] consumes all characters until a newline character is found and discards them. That way no garbage is left in the input buffer after scanf(). Note that this will consider successful conversions followed by garbage valid input. If you'd want to treat that as an input error you'd have to use more sophisticated methods.