Home > Software engineering >  Do while can do this but I want to know why this is wrong
Do while can do this but I want to know why this is wrong

Time:10-03

#include <stdio.h>
#include <rpcndr.h>



int main() {
    boolean playAgain=1;
    char playInput;
    float num1,num2,answer;

    char operator;

    while (playAgain){
        printf("Enter First Number, operator, second number:");
        scanf("%f%c%f",&num1,&operator,&num2);

        switch (operator) {
            case '*':
                answer=(num1*num2);
                break;
            case '/':
                answer=(num1/num2);
                break;
            case ' ':
                answer=num1 num2;
                break;
            case '-':
                answer=num1-num2;
                break;
            default:break;
        }
        printf("%f\n",answer);
        printf("Do You Want To Try It Again(y/n)?");
        scanf("%c",&playInput);
        if(playInput=='n'){
            playAgain=0;
        }
    }




}

Do while can do this code. But I want to why this method gets an error. And there is a problem with Scanf() function. Error says : Clang-Tidy: 'scanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtof' instead

CodePudding user response:

There are some issues.

The first scanf won't check for syntax errors in the numbers and may leave a newline in the stream and confuse the second scanf

The second scanf may not strip the newline from the stream, so on the second loop iteration, the first scanf may have a problem.

While it might be possible to fix/contort scanf into doing what you want, I'd follow clang's warning and use strtof.

Here's the code refactored to use fgets and strtof. It is annotated:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <rpcndr.h>

// lineget -- get a line from user with prompt
// RETURNS: pointer to buffer (NULL means EOF)
char *
lineget(char *buf,size_t len,const char *prompt)
{
    char *cp;

    // output prompt to user
    puts(prompt);
    fflush(stdout);

    do {
        // get an input line
        cp = fgets(buf,len,stdin);

        // got EOF
        if (cp == NULL)
            break;

        // strip newline
        buf[strcspn(buf,"\n")] = 0;
    } while (0);

    return cp;
}

int
main(void)
{
    float num1, num2, answer;
    char *cp;
    char buf[1000];
    int err;

    char operator;

    while (1) {
        cp = lineget(buf,sizeof(buf),
            "Enter First Number, operator, second number:");
        if (cp == NULL)
            break;

        // get the first number
        num1 = strtof(cp,&cp);

        // get the operator
        // NOTE: this could be a syntax error for the first number -- we'll
        // check that below in the switch
        operator = *cp;
        if (operator != 0)
              cp;

        // get second number and check for syntax error
        num2 = strtof(cp,&cp);
        if (*cp != 0) {
            printf("ERROR trailing '%s'\n",cp);
            continue;
        }

        err = 0;
        switch (operator) {
        case '*':
            answer = (num1 * num2);
            break;
        case '/':
            answer = (num1 / num2);
            break;
        case ' ':
            answer = num1   num2;
            break;
        case '-':
            answer = num1 - num2;
            break;
        default:
            err = 1;
            break;
        }

        // we got a bad operator (or syntax error in first number)
        if (err) {
            printf("ERROR unknown operator '%c'\n",operator);
            continue;
        }

        printf("%f\n", answer);

        cp = lineget(buf,sizeof(buf),"Do You Want To Try It Again(y/n)?");
        if (cp == NULL)
            break;
        if (buf[0] == 'n')
            break;
    }

    return 0;
}

UPDATE:

The above code will detect most errors. Here's an enhanced version that does even more explicit checking:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <rpcndr.h>

// lineget -- get a line from user with prompt
// RETURNS: pointer to buffer (NULL means EOF)
char *
lineget(char *buf,size_t len,const char *prompt)
{
    char *cp;

    // output prompt to user
    puts(prompt);
    fflush(stdout);

    do {
        // get an input line
        cp = fgets(buf,len,stdin);

        // got EOF
        if (cp == NULL)
            break;

        // strip newline
        buf[strcspn(buf,"\n")] = 0;
    } while (0);

    return cp;
}

int
main(void)
{
    float num1, num2, answer;
    char *cp;
    char *bp;
    char buf[1000];
    int err;

    char operator;

    while (1) {
        bp = lineget(buf,sizeof(buf),
            "Enter First Number, operator, second number:");
        if (bp == NULL)
            break;

        // get the first number
        num1 = strtof(bp,&cp);

        // ensure we got at least a digit
        // NOTE: this will detect:
        //   ""
        //   "j"
        if (cp == bp) {
            printf("ERROR no first number specified\n");
            continue;
        }

        // get the operator
        // NOTE: this could be a syntax error for the first number -- we'll
        // check that below in the switch
        operator = *cp;

        // no operator specified
        if (operator == 0) {
            printf("ERROR no operator specified\n");
            continue;
        }

        // skip over the operator
        bp =   cp;

        // get second number and check for syntax error
        num2 = strtof(bp,&cp);
        if (*cp != 0) {
            printf("ERROR trailing '%s'\n",cp);
            continue;
        }

        // we need at least one digit (e.g.):
        //   we want to reject: 23  and ensure we have [at least] 23 0
        //   this will detect 23 k
        if (cp == bp) {
            printf("ERROR no second number specified\n");
            continue;
        }

        err = 0;
        switch (operator) {
        case '*':
            answer = (num1 * num2);
            break;
        case '/':
            answer = (num1 / num2);
            break;
        case ' ':
            answer = num1   num2;
            break;
        case '-':
            answer = num1 - num2;
            break;
        default:
            err = 1;
            break;
        }

        // we got a bad operator (or syntax error in first number)
        if (err) {
            printf("ERROR unknown operator '%c'\n",operator);
            continue;
        }

        printf("%f\n", answer);

        cp = lineget(buf,sizeof(buf),"Do You Want To Try It Again(y/n)?");
        if (cp == NULL)
            break;
        if (buf[0] == 'n')
            break;
    }

    return 0;
}

CodePudding user response:

You should use fflush(stdin) function to clear all the leftover data in the stdin buffer (or clear it another way), otherwise scanf will read an extra \n at the end causing it to skip the rest of the data in the buffer

#include <stdio.h>

int main() {
    _Bool playAgain=1;
    char playInput;
    float num1,num2,answer;

    char operator;

    while (playAgain){
        printf("Enter First Number, operator, second number:");
        scanf("%f%c%f",&num1,&operator,&num2);
        fflush(stdin);
        switch (operator) {
            case '*':
                answer=(num1*num2);
                break;
            case '/':
                answer=(num1/num2);
                break;
            case ' ':
                answer=num1 num2;
                break;
            case '-':
                answer=num1-num2;
                break;
            default:break;
        }
        printf("%f\n",answer);
        printf("Do You Want To Try It Again(y/n)?");
        scanf("%c",&playInput);
        fflush(stdin);
        if(playInput=='n'){
            playAgain=0;
        }
    }
}
  • Related