2

I am curently learning C Programming in University and I got the task to write a program witch puts out the interception points with 0 / x-axis of any function ax^2+bx+c.

In order to make sure that the input is correct (int, float, etc.) I run the following while loop. Prior to that a is definded as a double.

printf("Input for a=");

while (scanf("%lf", &a) == 0)
{
    fflush(stdin);
    scanf("%lf", &a);
    printf("Incorrect Input! New Input needed.\n"); 
    printf("a=");
}

I am aware that the fflush(stdin) operator only clears the buffer when a second input function occurs and therefore the fflush inside the loop does not clear the buffer and therefore the condition of the loop is always true and thus I created an infinite loop.

My professor also forbids the use of goto, which is why I am here, because I can't come up with a reasonable solution that solves this problem. I also tried and failed with:

do
{
    printf("\nInput for a= ");
    scanf("%lf", &a);
}

while (isdigit(a));
{
    printf("Thank you.\n");
}

With this arrangement I get the failure notification: Expression c >= -1 && <= 255. I guess this has to do with false variable definition (double, digit) or such.

However my original question was whether there is an elegant solution to this problem or at least any solution.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • 3
    `fflush(stdin)` is undefined behaviour, don't do it. – Jabberwocky Dec 11 '18 at 14:56
  • In case you are interested in the whole program as of now: https://pastebin.com/0ELkCiS7 – Lukas Langer Dec 11 '18 at 14:57
  • Before doing anything else, please [indent](https://en.wikipedia.org/wiki/Indentation_style) your program properly. This is is very important, not so much for us but rather four _you_. – Jabberwocky Dec 11 '18 at 14:59
  • Are you asking how to take input and distinguish between an `int` and `float` with the same `scanf` statement? Or, are you simply wanting to limit input to one or the other? In either case it is better to enclose the block in a continual loop, e.g. `for (;;) { int rtn = scanf (...); ... }` where you validate the return (`rtn`) and if the input is good, simply `break;`. Why? `scanf` can return `0` or `EOF` on failure and controlling the loop with `== 0` will fail to detect `EOF`. – David C. Rankin Dec 11 '18 at 15:04
  • @DavidC.Rankin Im am asking how to distinguish between all numbers and every other possible input for scanf. I want the scanf function to only assign the input value to the variable if the input is correct. – Lukas Langer Dec 11 '18 at 15:11
  • @Jabberwocky Thank you, I may work on that :) – Lukas Langer Dec 11 '18 at 15:12
  • @Jabberwocky what alternatives are there for fflush? – Lukas Langer Dec 11 '18 at 15:14
  • Most `scanf` formats ignore leading whitespace anyway if that is the purpose of trying to flush the input. A noteable exception is `"%c"` where adding a space before `" %c"` will cause whitespace to be filtered. But if th input was say `"a"` then you'll need to flush the buffer. Note: it is **vital** to check the return value from `scanf` function family (number of items scanned). – Weather Vane Dec 11 '18 at 15:19
  • ... note too that you call `scanf` twice in the loop, what is that for? – Weather Vane Dec 11 '18 at 15:24
  • @WeatherVane I thought I could "override" the variable a with another scanf input.. – Lukas Langer Dec 11 '18 at 18:48
  • 1
    Once you have scanned it it has gone. Reading a line with `fgets` is more flexible. If one `sscanf` doesn't work, you can try again with another. And it is easy to just dump that line and read another one. – Weather Vane Dec 11 '18 at 19:51

1 Answers1

5

Lukas, I'm still not 100% clear and your:

"Im am asking how to distinguish between all numbers and every other possible input for scanf."

scanf can provide conversion to a single given type based on the conversion specifier used, so you can't read both int and float with the same scanf statement and a single conversion specifier. (now you can read a line with, e.g. fgets and then use alternate sscanf statements and check the remaining characters to do that)

That said, I think I understand what you are asking and can answer both the fflush and read of a value questions.

To begin, when using scanf you must check the return and handle three-cases. (1) EOF, the user cancels input with a Ctrl+d (or Ctrl+z on windows); (2) a matching or input failure occurs resulting in a return of less than the number of conversion specifiers used; and finally (3) the good input case (where you impose any additional checks you have, e.g. positive, less than 100, etc..)

While fflush(stdin) is undefined behavior on most systems, there are a number of implementations that allow it. (primarily windows, but Linux allows it for seekable stream, e.g. a file redirected on stdin) Bottom line, it isn't portable without caveats, so it's best to provide a simple equivalent with getchar(), e.g.

void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

As for scanf, as I mentioned in the comment, it is far easier to enclose the scanf call within an infinite loop which you only break when the input satisfies all your constraints. A simple example requiring integer input would be:

int getint (int *value, const char *prompt)
{
    /* loop continually until good input or canceled */
    for (;;) {
        int rtn;
        fputs (prompt, stdout);     /* display prompt */
        rtn = scanf ("%d", value);

        if (rtn == EOF) {   /* user generated manual EOF */
            fputs ("<user canceled input>\n", stderr);
            return 0;
        }
        empty_stdin();  /* all other cases - empty input buffer */
        if (rtn == 1)   /* good input, break */
            break;
        /* otherwise matching failure */
        fputs ("  error: invalid integer input.\n", stderr);
    }
    return *value;  /* value also availale through pointer */
}

Putting it altogether in a simple example, you would have:

#include <stdio.h>

void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

int getint (int *value, const char *prompt)
{
    /* loop continually until good input or canceled */
    for (;;) {
        int rtn;
        fputs (prompt, stdout);     /* display prompt */
        rtn = scanf ("%d", value);

        if (rtn == EOF) {   /* user generated manual EOF */
            fputs ("<user canceled input>\n", stderr);
            return 0;
        }
        empty_stdin();  /* all other cases - empty input buffer */
        if (rtn == 1)   /* good input, break */
            break;
        /* otherwise matching failure */
        fputs ("  error: invalid integer input.\n", stderr);
    }
    return *value;  /* value also availale through pointer */
}

int main (void) {

    int v,
        i = getint (&v, "enter integer value: ");

    if (i)
        printf ("\ninteger: %d\n", v);

    return 0;
}

Example Use/Output

Where you can now do your best to break any input routine you write. If you find a problem, go fix it and try to break it again.

The code above allows for fairly robust input of any one given type of value, e.g.

$ ./bin/scanfint
enter integer value: no
  error: invalid integer input.
enter integer value: apples, banannas, and pears
  error: invalid integer input.
enter integer value: 21

integer: 21

Look things over and let me know if your question was slightly different, or if you have further questions about the answer.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thanks for this very detailed awnser! I think this will solve my problem. If I still cant solve it I may come back.. Thanks anyways! – Lukas Langer Dec 11 '18 at 18:50
  • 2 ideas: A `fflush(stdout);` between `fputs (prompt, stdout);` `rtn = scanf ("%d", value);` makes certain prompt is seen before input. When `empty_stdin();` returns due to `ferror()`, I'd expect a return from `getint()` like in `if (rtn == EOF)`, but that is a subtle point. – chux - Reinstate Monica Jul 13 '20 at 09:37
  • I was not suggesting `empty_stdin()` between `fputs (prompt, stdout);` `rtn = scanf ("%d", value);`. I was suggesting `fflush(stdout);` – chux - Reinstate Monica Jul 13 '20 at 21:38
  • Okay, that makes more sense. I read it in a hurry and thought you have used a shorthand for flushing `stdin`, yes flushing `stdout` there makes sense. However, since `scanf` is an input function that would force a flush of `stdout` at that point. Wouldn't it be repetitive there? – David C. Rankin Jul 13 '20 at 21:45