Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flushing stdin after every input - which approach is not buggy?

Tags:

c

flush

After Mark Lakata pointed out that the garbage isn't properly defined in my question I came up with this. I'll keep this updated to avoid confusions.


I am trying to get a function that I can call before a prompt for user input like printf("Enter your choice:); followed a scanf and be sure that only the things entered after the prompt would be scanned in by scanf as valid input.

As far as I can understand the function that is needed is something that flushes standard input completely. That is what I want. So for the purpose of this function the "garbage" is everything in user input i.e. the whole user input before that user prompt.


While using scanf() in C there is always the problem of extra input lying in the input buffer. So I was looking for a function that I call after every scanf call to remedy this problem. I used this, this, this and this to get these answers

//First approach
scanf("%*[^\n]\n");

//2ndapproach
scanf("%*[^\n]%*c");

//3rd approach
int c;
while((c = getchar()) != EOF) 
    if (c == '\n') 
        break;

All three are working as far as I could find by hit-and-trial and going by the references. But before using any of these in all of my codes I wanted to know whether any of these have any bugs?

EDIT:

Thanks to Mark Lakata for one bug in 3rd. I corrected it in the question.

EDIT2:

After Jerry Coffin answered I tested the 1st 2 approaches using this program in code:blocks IDE 12.11 using GNU GCC Compiler(Version not stated in the compiler settings).

#include<stdio.h>

int main()
{
    int x = 3; //Some arbitrary value
    //1st one
    scanf("%*[^\n]\n");
    scanf("%d", &x);
    printf("%d\n", x);

    x = 3;
    //2nd one
    scanf("%*[^\n]%*c");
    scanf("%d", &x);
    printf("%d", x);
}

I used the following 2 inputs

First Test Input (2 Newlines but no spaces in the middle of garbage input)

abhabdjasxd


23
bbhvdahdbkajdnalkalkd



46

For the first I got the following output by the printf statements

23
46

i.e. both codes worked properly.

Second Test input: (2 Newlines with spaces in the middle of garbage input)

hahasjbas asasadlk


23
manbdjas sadjadja a


46

For the second I got the following output by the printf statements

23
3

Hence I found that the second one won't be taking care of extra garbage input whitespaces. Hence, it isn't foolproof against garbage input.

I decided to try out a 3rd test case (garbage includes newline before and after the non-whitespace character)

``
hahasjbas asasadlk


23

manbdjas sadjadja a


46

The answer was

3
3

i.e. both failed in this test case.

like image 628
Aseem Bansal Avatar asked Jun 22 '13 07:06

Aseem Bansal


People also ask

When should I flush Stdin?

The function fflush(stdin) is used to flush or clear the output buffer of the stream. When it is used after the scanf(), it flushes the input buffer also. It returns zero if successful, otherwise returns EOF and feof error indicator is set.

Why do we need fflush() in C?

fflush() is typically used for output stream only. Its purpose is to clear (or flush) the output buffer and move the buffered data to console (in case of stdout) or disk (in case of file output stream).

What does fflush() do?

The fflush() function causes the system to empty the buffer that is associated with the specified output stream, if possible. If the stream is open for input, the fflush() function undoes the effect of any ungetc() function. The stream remains open after the call. If stream is NULL, the system flushes all open streams.


2 Answers

The first two are subtly different: they both read and ignore all the characters up to a new-line. Then the first skips all consecutive white space so after it executes, the next character you read will be non-whitespace.

The second reads and ignores characters until it encounters a new-line then reads (and discards) exactly one more character.

The difference will show up if you have (for example) double-spaced text, like:

line 1

line 2

Let's assume you read to somewhere in the middle of line 1. If you then execute the first one, the next character you read in will be the 'l' on line 2. If you execute the second, the next character you read in will be the new-line between line 1 and line 2.

As for the third, if I were going to do this at all, I'd do something like:

int ch;
while ((ch=getchar()) != EOF && ch != '\n')
    ;

...and yes, this does work correctly -- && forces a sequence point, so its left operand is evaluated first. Then there's a sequence point. Then, if and only if the left operand evaluated to true, it evaluates its right operand.

As for performance differences: since you're dealing with I/O to start with, there's little reasonable question that all of these will always be I/O bound. Despite its apparent complexity, scanf (and company) are usually code that's been used and carefully optimized over years of use. In this case, the hand-rolled loop may be quite a bit slower (e.g., if the code for getchar doesn't get expanded inline) or it may be about the same speed. The only way it stands any chance of being significantly faster is if the person who wrote your standard library was incompetent.

As far maintainability: IMO, anybody who claims to know C should know the scan set conversion for scanf. This is neither new nor rocket science. Anybody who doesn't know it really isn't a competent C programmer.

like image 89
Jerry Coffin Avatar answered Sep 21 '22 02:09

Jerry Coffin


The first 2 examples use a feature of scanf that I didn't even know existed, and I'm sure a lot of other people didn't know. Being able to support a feature in the future is important. Even if it was a well known feature, it will be less efficient and harder to read the format string than your 3rd example.

The third example looks fine.

(edit history: I made a mistake saying that ANSI-C did not guarantee left-to-right evaluation of && and proposed a change. However, ANSI-C does guarantee left-to-right evaluation of &&. I'm not sure about K&R C, but I can't find any reference to it and no one uses it anyways...)

like image 43
Mark Lakata Avatar answered Sep 19 '22 02:09

Mark Lakata