NO

Author Topic: Flushing stdin  (Read 6871 times)

Cnoob

  • Guest
Flushing stdin
« on: April 15, 2015, 12:02:56 AM »
Hi

Since joining, I have received some great help here so it's time to give back a little which may be of some help to other C noobs like me.
Below is some simple code, syntax wise it's fine and compiles with no errors, however when running it, it behaves very strangely:

Code: [Select]
#include <stdio.h>
 #include <stdlib.h>
   

int main(){

char StringOne[2];
char StringTwo[10];

printf("Hold down a letter key until you get lots of characters on the screen\n");
fgets(StringOne, 2, stdin);
printf("%s\n", StringOne, "\n" );

printf("Do it again\n");
  fgets(StringTwo, 10, stdin);
  printf("%s\n", StringOne, "\n" );

return 0;
}

Notice anything strange?

Now this one:

Code: [Select]
#include <stdio.h>
#include <stdlib.h>
   

int main(){

char StringOne[2];
char StringTwo[10];

printf("Hold down a key until you get lots of characters on the screen\n");
fgets(StringOne, 2, stdin);

fflush(stdin);

printf("%s\n", StringOne, "\n" );

printf("Do it again\n");
fgets(StringTwo, 10, stdin);

fflush(stdin);

printf("%s\n", StringOne, "\n" );

return 0;
}

Now it works perfectly.
I suppose to the seasoned C programmer, it's only natural and obvious to flush stdin after using things like fgets or getc but not to the newbie and it's not something that is often hinted at,
at least not on the sites I looked at.

Anyway, hope it's of some help to others.
« Last Edit: April 15, 2015, 12:04:43 AM by Cnoob »

Snowman

  • Guest
Re: Flushing stdin
« Reply #1 on: April 15, 2015, 12:20:33 AM »

Cnoob

  • Guest
Re: Flushing stdin
« Reply #2 on: April 15, 2015, 12:45:06 AM »
Hi Snowman

Interesting links.
I like the one solution presented:

Code: [Select]
while((c = getchar()) != '\n' && c != EOF)
/* discard */ ;

They also mention that fflush(stdin) is not portable, fair enough but it is available and supported in Pelles, so if one is coding only for the Windows environment, why would portability be a top priority?
Am I missing something?


EDIT:

Also just tried fflush(stdin) in CodeBlocks which uses gcc and it works.
« Last Edit: April 15, 2015, 12:48:59 AM by Cnoob »

Snowman

  • Guest
Re: Flushing stdin
« Reply #3 on: April 15, 2015, 01:24:32 AM »
I guess this is where Practice and Theory collide, and you have to choose a side, practical shrewdness or theoretical correctness:
In practice, fflush() is usually coded to also work for input streams and conveniently discard data.
In theory, you can not and must not rely on that; at least not until the C standard changes to say you can.

Here's one more link against fflush(stdin) being used. And another. And one more.

Cnoob

  • Guest
Re: Flushing stdin
« Reply #4 on: April 15, 2015, 01:39:38 AM »
I agree that it may not be available under all conditions and operating systems, but on those that do, fflush seems like the easiest way although not the best coding practice.
However I have to wonder what percentage of people that program in C, do so for Windows, Linux, Mac and Android, probably a very small percentage.

Never the less, if one has to cater portability, would this not then be a good solution?
(adapted from one of the sites you posted a link to):

Code: [Select]
void clearInput()
{
       char junk[255];
       fgets(junk , 255, stdin);
}

Then it's just a matter of calling clearInput() after every fgets.

Your thoughts on how good a solution this could be?
Tried it in both Pelles and gcc in palce of fflush and it seems to do the trick.

Snowman

  • Guest
Re: Flushing stdin
« Reply #5 on: April 15, 2015, 09:57:20 AM »
My first thought upon seeing clearInput() was: "what if there are more than 255 characters of junk?"
In the comp.lang.c FAQ example, the code discards chars until it finds newline or EOF.

At this point I would say just use fflush(stdin) but be sure to document it, possibly as a warning.

Quote from: Pelles C Help
fflush function

Purpose:
Forces buffered data for a stream to disk.

Syntax:
int fflush(FILE *stream);

Declared in:
<stdio.h>

Description:
 ...
If stream is the predefined stdin stream, the read buffers are cleared [not standard C] [fixed 2.90.1].

Small trivia:

If your code gives you a warning such as:
warning #2117: Old-style function definition for 'main'.
it is because your main() ought to be defined as:

Code: [Select]
int main(void)
{
    // ...
}

This is a small difference between C and C++. In C, if the argument list for a function is empty it means that it takes an arbitrary number of arguments. So you need to use void to mean that it takes no arguments.

Cnoob

  • Guest
Re: Flushing stdin
« Reply #6 on: April 15, 2015, 01:17:26 PM »

Small trivia:

If your code gives you a warning such as:
warning #2117: Old-style function definition for 'main'.
it is because your main() ought to be defined as:

Code: [Select]
int main(void)
{
    // ...
}

This is a small difference between C and C++. In C, if the argument list for a function is empty it means that it takes an arbitrary number of arguments. So you need to use void to mean that it takes no arguments.

I actually haven't come across that warning but it's a good point you make, thanks, from now on I will be using "int main(void)" as standard.

migf1

  • Guest
Re: Flushing stdin
« Reply #7 on: May 31, 2015, 11:38:43 AM »
...
I suppose to the seasoned C programmer, it's only natural and obvious to flush stdin after using things like fgets or getc but not to the newbie and it's not something that is often hinted at,
at least not on the sites I looked at.

Anyway, hope it's of some help to others.

Flushing the input stream is not always desirable (piping may be one reason). For interactive-input though (prompting), we most probably need it. However, interactive-input can be pretty tedious in C, as this old but excellent article explains in details.

Fortunately, it is not hard to make a variation of fgets() which also flushes the input stream. Something along the following lines (I've put lots of comments for your convenience):

Code: [Select]
/* ----------------------------------------------------
 * Read a c-string from stdin, flushing any extra characters. Return NULL on error.
 *
 * Basic differences over gets():
 *  - performs sanity checks
 *  - limits the number of input chars
 *  - flushes any extra chars from stdin
 */
char *my_gets( char *s, size_t sz )
{
    int c;
    size_t i;

    /* sanity checks */
    if ( !s || sz < 1 ) {
        return NULL;
    }

    /* read chars from stdin until sz-1, EOL or EOF */
    for (i=0; i < sz-1 && '\n' != (c=getchar()) && EOF != c; i++) {
        s[i] = c;
    }

    /* sz reached without '\n' ? */
    if ( '\0' != s[i] && '\n' != s[i] ) {
        /* flush any remaining chars */ 
        while ( '\n' != getchar() ) {
            /* void */ ;
        }
    }

    /* nul-terminate s and return it */
    s[i] = '\0';   
    return s;
}