how scanf leaves stdin in case of input error?

Discussion in 'C Programming' started by Alexo, Mar 9, 2011.

  1. Alexo

    Alexo Guest

    hello everyone,
    consider my simple example:

    /* code starts here */

    #include <stdio.h>

    int main(void){

    int a = 0, b = 0;

    do{
    printf("\ninsert an integer number ");
    b = scanf("%d", &a);
    }while( b != 1 );

    printf("\nThe number inserted is %d", a);

    return 0;
    }

    /* code ends here */

    try to execute it inputing a character instead of an integer number. The
    loop never ends.
    I would like that the loop be executed only once when the scanf fails.
    Thank you in advance
     
    Alexo, Mar 9, 2011
    #1
    1. Advertising

  2. Alexo

    John Gordon Guest

    In <vzSdp.4941$%> "Alexo" <> writes:

    > do{
    > printf("\ninsert an integer number ");
    > b = scanf("%d", &a);
    > }while( b != 1 );


    > try to execute it inputing a character instead of an integer number. The
    > loop never ends.
    > I would like that the loop be executed only once when the scanf fails.
    > Thank you in advance


    If you want the input prompt to appear only once, then why did you put
    it inside a loop?

    Perhaps you want something more like this:

    char input_buffer[99];

    printf("\ninsert an integer number ");
    fflush(stdout);
    fgets(input_buffer, sizeof(input_buffer), stdin);
    b = sscanf(input_buffer, "%d", &a);
    if (b == 1)
    printf("\nThe number inserted is %d\n", a);
    else
    printf("You did not enter a number. Shame on you!\n");

    --
    John Gordon A is for Amy, who fell down the stairs
    B is for Basil, assaulted by bears
    -- Edward Gorey, "The Gashlycrumb Tinies"
     
    John Gordon, Mar 9, 2011
    #2
    1. Advertising

  3. Alexo

    Eric Sosman Guest

    On 3/9/2011 4:47 PM, Alexo wrote:
    > hello everyone,
    > consider my simple example:
    >
    > /* code starts here */
    >
    > #include<stdio.h>
    >
    > int main(void){
    >
    > int a = 0, b = 0;
    >
    > do{
    > printf("\ninsert an integer number ");
    > b = scanf("%d",&a);
    > }while( b != 1 );
    >
    > printf("\nThe number inserted is %d", a);
    >
    > return 0;
    > }
    >
    > /* code ends here */
    >
    > try to execute it inputing a character instead of an integer number. The
    > loop never ends.
    > I would like that the loop be executed only once when the scanf fails.


    This is Question 12.9 in the comp.lang.c Frequently Asked
    Questions (FAQ) list at <http://www.c-faq.com/>.

    --
    Eric Sosman
    d
     
    Eric Sosman, Mar 10, 2011
    #3
  4. Alexo

    Alexo Guest

    ----- Original Message -----
    From: "John Gordon" <>
    Newsgroups: comp.lang.c
    Sent: Wednesday, March 09, 2011 11:03 PM
    Subject: Re: how scanf leaves stdin in case of input error?


    > If you want the input prompt to appear only once, then why did you put
    > it inside a loop?


    the loop is required to prompt for each time you give an incorrect answer

    > Perhaps you want something more like this:
    >
    > char input_buffer[99];
    >
    > printf("\ninsert an integer number ");
    > fflush(stdout);
    > fgets(input_buffer, sizeof(input_buffer), stdin);
    > b = sscanf(input_buffer, "%d", &a);
    > if (b == 1)
    > printf("\nThe number inserted is %d\n", a);
    > else
    > printf("You did not enter a number. Shame on you!\n");


    yes that's right! That solves my problem reading directly from stdin qith
    scanf.
    So the modified dummy program is now:

    #include <stdio.h>

    #define DIM 256

    int main(void)
    {
    int a = 0, b = 0;
    char input_buffer[DIM];

    do{
    printf("\ninsert an integer number ");
    fgets(input_buffer, DIM, stdin);
    b = sscanf(input_buffer, "%d", &a);
    }while( b != 1 );

    printf("\nThe number inserted is %d", a);

    return 0;
    }
     
    Alexo, Mar 10, 2011
    #4
  5. "Alexo" <> writes:

    > ----- Original Message -----
    > From: "John Gordon" <>
    > Newsgroups: comp.lang.c
    > Sent: Wednesday, March 09, 2011 11:03 PM
    > Subject: Re: how scanf leaves stdin in case of input error?
    >
    >
    >> If you want the input prompt to appear only once, then why did you put
    >> it inside a loop?

    >
    > the loop is required to prompt for each time you give an incorrect answer
    >
    >> Perhaps you want something more like this:
    >>
    >> char input_buffer[99];
    >>
    >> printf("\ninsert an integer number ");
    >> fflush(stdout);
    >> fgets(input_buffer, sizeof(input_buffer), stdin);
    >> b = sscanf(input_buffer, "%d", &a);
    >> if (b == 1)
    >> printf("\nThe number inserted is %d\n", a);
    >> else
    >> printf("You did not enter a number. Shame on you!\n");

    >
    > yes that's right!


    It's better but not quite right because fgets can fail. That's
    particularly a problem when you put it in a loop...

    > That solves my problem reading directly from stdin qith
    > scanf.
    > So the modified dummy program is now:
    >
    > #include <stdio.h>
    >
    > #define DIM 256
    >
    > int main(void)
    > {
    > int a = 0, b = 0;
    > char input_buffer[DIM];
    >
    > do{
    > printf("\ninsert an integer number ");
    > fgets(input_buffer, DIM, stdin);


    John Gordon's sizeof input_buffer is (to my mind) better than repeating
    DIM here.

    > b = sscanf(input_buffer, "%d", &a);
    > }while( b != 1 );
    >
    > printf("\nThe number inserted is %d", a);
    >
    > return 0;
    > }


    You have undefined behaviour now when an input error occurs and a
    possible infinite loop when the end of the input is detected. I think
    it is a good idea to get into the habit of handling these cases[1].

    The fundamental problem is that there may be no integer forthcoming (no
    matter how much you bug the user with a prompt) and you should start out
    by deciding what to do in that situation. One way to settle that is to
    say that you'll keep prompting until fgets reports that it can't get any
    new data at which point you give up (maybe with a message saying why).

    [1] There are cases of undefined behaviour when the input integer causes
    and overflow, but I think it is perfectly reasonable to put those to one
    side at this stage in learning.

    --
    Ben.
     
    Ben Bacarisse, Mar 10, 2011
    #5
  6. pete <> writes:

    <snip>
    > new.c does what you want.
    > I wrote it to help you to get a better understanding of
    > how scanf leaves stdin in case of input error.
    >
    > /* BEGIN new.c */
    >
    > #include <stdio.h>
    >
    > int
    > main(void)
    > {
    > int a, b;
    >
    > do {
    > printf("\ninsert an integer number ");
    > fflush(stdout);
    > b = scanf("%d", &a);
    > if (b == 0) {
    > do {
    > a = getchar();
    > } while (a != '\n' && a != EOF);
    > puts("That's no good.");
    > }
    > } while( b != 1 );
    > printf("\nThe number inserted is %d\n", a);
    > return 0;
    > }
    >
    > /* END new.c */


    You get an infinite loop when the data stream ends with no integer.

    --
    Ben.
     
    Ben Bacarisse, Mar 10, 2011
    #6
  7. pete <> writes:

    <snip>
    > This version show it better, I think,
    > especially if you input a few characters of text.

    <snip>
    > int a, b;
    >
    > do {
    > printf("\ninsert an integer number ");
    > fflush(stdout);
    > b = scanf("%d", &a);
    > if (b == 0) {
    > puts("After that last scanf call, stdin contained");
    > do {
    > a = getchar();
    > printf("%c ");


    printf what? If you print a, you need t handle a == EOF.

    > } while (a != '\n' && a != EOF);


    I'd use a while loop:

    while ((a = getchar()) != '\n' && a != EOF)
    putchar(a);

    > puts("\nThat's no good.");
    > }
    > } while( b != 1 );
    > printf("\nThe number inserted is %d\n", a);
    > return 0;
    > }
    >
    > /* END new.c */


    --
    Ben.
     
    Ben Bacarisse, Mar 10, 2011
    #7
  8. Alexo

    James Waldby Guest

    On Thu, 10 Mar 2011 14:05:01 +0000, Alexo wrote:
    > From: "John Gordon" ...
    >> If you want the input prompt to appear only once, then why did you put
    >> it inside a loop?

    >
    > the loop is required to prompt for each time you give an incorrect
    > answer
    >
    >> Perhaps you want something more like this:

    [snip JG code]
    > yes that's right! That solves my problem reading directly from stdin
    > qith scanf.
    > So the modified dummy program is now:
    >
    > #include <stdio.h>
    >
    > #define DIM 256
    >
    > int main(void)
    > {
    > int a = 0, b = 0;
    > char input_buffer[DIM];
    >
    > do{
    > printf("\ninsert an integer number "); fgets(input_buffer, DIM,
    > stdin);
    > b = sscanf(input_buffer, "%d", &a);
    > }while( b != 1 );
    >
    > printf("\nThe number inserted is %d", a);
    >
    > return 0;
    > }


    When given an empty input file on my system, your program
    generates output about 4.5 times faster than Pete's program
    [as posted 10 Mar 2011 08:53:24 -0600, with printf("%c ");
    changed to printf("%c ",a );].

    I ran both of them as below [where ~> represents shell prompt]
    and pressed control-C after a few seconds to interrupt execution.
    Yours produces ~ 22MB per elapsed second, his gives 4.9MBpes.

    ~> time ./scanf-alexo < /dev/null > t; wc t

    real 0m4.615s
    user 0m1.918s
    sys 0m2.670s
    3854652 15418605 100220928 t

    ~> time ./scanf-pete < /dev/null > t; wc t

    real 0m4.823s
    user 0m0.538s
    sys 0m4.279s
    800711 3202844 20818486 t

    ~> tail t
    insert an integer number
    insert an integer number
    insert an integer number
    insert an integer number
    insert an integer number
    insert an integer number
    insert an integer number
    insert an integer number
    insert an integer number
    insert an integer number

    --
    jiw
     
    James Waldby, Mar 10, 2011
    #8
  9. Alexo

    Tobias Blass Guest

    On Fri, 11 Mar 2011, pete wrote:

    >Ben Bacarisse wrote:
    >>
    >> pete <> writes:
    >>
    >> <snip>
    >> > This version show it better, I think,
    >> > especially if you input a few characters of text.

    >> <snip>
    >> > int a, b;
    >> >
    >> > do {
    >> > printf("\ninsert an integer number ");
    >> > fflush(stdout);
    >> > b = scanf("%d", &a);
    >> > if (b == 0) {
    >> > puts("After that last scanf call, stdin contained");
    >> > do {
    >> > a = getchar();
    >> > printf("%c ");

    >>
    >> printf what? If you print a, you need t handle a == EOF.

    >
    >Thank you again.
    >
    >The strange thing is that
    >when I tested it on my machine,
    >it printed a.
    >


    This is just coincidence I think. Printf looks for its argument on the stack
    (where your parameter is supposed to be) and finds a there.
     
    Tobias Blass, Mar 11, 2011
    #9
  10. Alexo

    Eric Sosman Guest

    On 3/11/2011 7:50 AM, pete wrote:
    > [...]
    > It's an interesting instance (interesting to me anyway)
    > of undefined behavior, because when I tested it
    > the exhibited behavior matched what I wanted the code to do,
    > even though I had not written code
    > to correctly state what I wanted to be done.


    Welcome to the programmer's daily existence.

    The gap between "What I said" and "What I meant," though both
    wide and deep, is easy to overlook. As programmers we all too
    frequently find ourselves in the middle of that gap, suspended over
    nothing like Wile E. Coyote suddenly realizing he's chased Roadrunner
    just a little too far and too recklessly ...

    --
    Eric Sosman
    d
     
    Eric Sosman, Mar 11, 2011
    #10
  11. Alexo

    James Kuyper Guest

    On 03/11/2011 07:50 AM, pete wrote:
    > Tobias Blass wrote:
    >>
    >> On Fri, 11 Mar 2011, pete wrote:
    >>
    >>> Ben Bacarisse wrote:
    >>>>
    >>>> pete<> writes:
    >>>>
    >>>> <snip>
    >>>>> This version show it better, I think,
    >>>>> especially if you input a few characters of text.
    >>>> <snip>
    >>>>> int a, b;
    >>>>>
    >>>>> do {
    >>>>> printf("\ninsert an integer number ");
    >>>>> fflush(stdout);
    >>>>> b = scanf("%d",&a);
    >>>>> if (b == 0) {
    >>>>> puts("After that last scanf call, stdin contained");
    >>>>> do {
    >>>>> a = getchar();
    >>>>> printf("%c ");
    >>>>
    >>>> printf what? If you print a, you need t handle a == EOF.
    >>>
    >>> Thank you again.
    >>>
    >>> The strange thing is that
    >>> when I tested it on my machine,
    >>> it printed a.
    >>>

    >>
    >> This is just coincidence I think.
    >> Printf looks for its argument on the stack
    >> (where your parameter is supposed to be) and finds a there.

    >
    > I understand.
    >
    > It's an interesting instance (interesting to me anyway)
    > of undefined behavior, because when I tested it
    > the exhibited behavior matched what I wanted the code to do,
    > even though I had not written code
    > to correctly state what I wanted to be done.


    Don't get too interested in such things; it's just a meaningless
    coincidence (though not a highly improbable one). The important thing to
    think about is avoiding writing such code, not trying to figure out why
    it accidentally worked.
    --
    James Kuyper
     
    James Kuyper, Mar 11, 2011
    #11
  12. Alexo

    Tobias Blass Guest

    On Fri, 11 Mar 2011, James Kuyper wrote:

    > On 03/11/2011 07:50 AM, pete wrote:
    >> Tobias Blass wrote:
    >>>
    >>> On Fri, 11 Mar 2011, pete wrote:
    >>>
    >>>> Ben Bacarisse wrote:
    >>>>>
    >>>>> pete<> writes:
    >>>>>
    >>>>> <snip>
    >>>>>> This version show it better, I think,
    >>>>>> especially if you input a few characters of text.
    >>>>> <snip>
    >>>>>> int a, b;
    >>>>>>
    >>>>>> do {
    >>>>>> printf("\ninsert an integer number ");
    >>>>>> fflush(stdout);
    >>>>>> b = scanf("%d",&a);
    >>>>>> if (b == 0) {
    >>>>>> puts("After that last scanf call, stdin contained");
    >>>>>> do {
    >>>>>> a = getchar();
    >>>>>> printf("%c ");
    >>>>>
    >>>>> printf what? If you print a, you need t handle a == EOF.
    >>>>
    >>>> Thank you again.
    >>>>
    >>>> The strange thing is that
    >>>> when I tested it on my machine,
    >>>> it printed a.
    >>>>
    >>>
    >>> This is just coincidence I think.
    >>> Printf looks for its argument on the stack
    >>> (where your parameter is supposed to be) and finds a there.

    >>
    >> I understand.
    >>
    >> It's an interesting instance (interesting to me anyway)
    >> of undefined behavior, because when I tested it
    >> the exhibited behavior matched what I wanted the code to do,
    >> even though I had not written code
    >> to correctly state what I wanted to be done.

    >
    > Don't get too interested in such things; it's just a meaningless coincidence
    > (though not a highly improbable one). The important thing to think about is
    > avoiding writing such code, not trying to figure out why it accidentally
    > worked.

    I think it _is_ interesting why it works. There's nothing wrong with getting
    interested in that. You should not use it in your code, though. But it's also
    useful to know becuase there are formatstring exploits that use this behavior.
    If you don't know how they work you can't prevent them. It may be enough to say
    "Never write printf(user_defined_string), use printf("%s",user_defined_string)"
    but I myself would want to know why.
     
    Tobias Blass, Mar 11, 2011
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    4
    Views:
    671
    Walter Roberson
    Sep 9, 2005
  2. =?ISO-8859-1?Q?Martin_J=F8rgensen?=

    scanf (yes/no) - doesn't work + deprecation errors scanf, fopen etc.

    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Feb 16, 2006, in forum: C Programming
    Replies:
    185
    Views:
    3,401
    those who know me have no need of my name
    Apr 3, 2006
  3. =?ISO-8859-1?Q?Martin_J=F8rgensen?=

    difference between scanf("%i") and scanf("%d") ??? perhaps bug inVS2005?

    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Apr 26, 2006, in forum: C Programming
    Replies:
    18
    Views:
    680
    Richard Bos
    May 2, 2006
  4. Tagore

    fflush(stdin), scanf and a space

    Tagore, Dec 28, 2008, in forum: C Programming
    Replies:
    5
    Views:
    4,887
    Eric Sosman
    Dec 29, 2008
  5. barry

    fflush(stdin), scanf and a space

    barry, Jun 7, 2010, in forum: C Programming
    Replies:
    5
    Views:
    6,129
    Eric Sosman
    Jun 9, 2010
Loading...

Share This Page