1

I have a comprehension question. I haven't used C for a long time, but today I rummaged through the C language threads a bit and I came across the following sentence which I roughly cite here: “scanf is reading a character which is left in the input stream from the previous input you type.” And something came into my memory. In those days, I used setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, BUFSIZ); to avoid this behavior. Those lines have ensured that there are no characters left from the previous input in the input stream.

My question now is: is that a good solution? This is something what I've always wanted to know

Daniel
  • 374
  • 1
  • 12
  • 2
    No. Using `setvbuf` [or `setbuf` or `setlinebuf`] on an _input_ stream is bad juju. It's only really designed for _output_ streams. Better to refactor your `scanf` format strings and/or calls to eliminate/correct the issue you're having. Please _edit_ your question and post your code in a code block [hopefully with some comments on the _expected_ behavior and/or rationale for the `setvbuf` calls]. Then, someone may be able to refactor the code to use only changes to the `scanf` calls to achieve the same effect [cleanly]. Possibly using (e.g.) `fgets` and then `sscanf` would give more control – Craig Estey Jun 05 '21 at 19:20
  • The problem you're having is one of the several reasons why you [shouldn't ever use the `*scanf` functions](https://stackoverflow.com/questions/58403537/what-can-i-use-for-input-conversion-instead-of-scanf). setvbuf will not help. – zwol Jun 05 '21 at 19:24
  • I think `stdin` is generally line-buffered by default, so that `_IONBF` might be changing its buffer mode. Not sure though. – mediocrevegetable1 Jun 05 '21 at 19:26
  • 1
    The [cppreference page for `setvbuf`](https://en.cppreference.com/w/c/io/setvbuf) says this: *This function may only be used after stream has been associated with an open file, but before any other operation (other than a failed call to setbuf/setvbuf).* – Adrian Mole Jun 05 '21 at 19:27

1 Answers1

2

I think what you're saying is you would make those two calls to setvbuf after a call to scanf, and that on at least one C implementation it had the effect of discarding any "left over" characters, that were not consumed by scanf, in stdin's buffer.

This is not a good way to discard "left over" characters, because the C standard says that setvbuf "may be used only after the stream has been associated with an open file and before any other operation (other than an unsuccessful call to setvbuf) is performed on the stream". (N1570 §7.21.5.6p2.) Violation of this rule should only cause setvbuf to fail, but nonetheless that means it doesn't do what you want.

The best way to do what you want is to not use scanf. Instead, write a real parser for your input and feed it either character by character (using getchar) or line by line (using getline or fgets). A real parser has principled handling of white space (including newline characters) and syntax errors, and therefore never finds itself with "left over" input.

Writing real parsers is unfortunately a book-length topic, so I will say only that sscanf is never useful in a real parser either. (Standard library functions that are useful include strsep, strchr, and the strto* family.)

There are rare circumstances (usually involving processing of interactive input) where you really do have to discard junk at the end of a line. In those circumstances, the way to do it is with this loop:

int c;
do c = getchar();
while (c != '\n' && c != EOF);
zwol
  • 135,547
  • 38
  • 252
  • 361
  • Thank you for your detailed explanation. I will use getline() in the future, or write myself a parser. (If I do something with C again). I just tried your 3 lines of code. Very nice. – Daniel Jun 05 '21 at 20:33