Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > how scanf leaves stdin in case of input error?

Reply
Thread Tools

how scanf leaves stdin in case of input error?

 
 
Alexo
Guest
Posts: n/a
 
      03-09-2011
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


 
Reply With Quote
 
 
 
 
John Gordon
Guest
Posts: n/a
 
      03-09-2011
In <vzSdp.4941$%(E-Mail Removed)> "Alexo" <(E-Mail Removed)> 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
http://www.velocityreviews.com/forums/(E-Mail Removed) B is for Basil, assaulted by bears
-- Edward Gorey, "The Gashlycrumb Tinies"

 
Reply With Quote
 
 
 
 
Eric Sosman
Guest
Posts: n/a
 
      03-10-2011
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
(E-Mail Removed)d
 
Reply With Quote
 
Alexo
Guest
Posts: n/a
 
      03-10-2011

----- Original Message -----
From: "John Gordon" <(E-Mail Removed)>
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;
}


 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      03-10-2011
"Alexo" <(E-Mail Removed)> writes:

> ----- Original Message -----
> From: "John Gordon" <(E-Mail Removed)>
> 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.
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      03-10-2011
pete <(E-Mail Removed)> 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.
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      03-10-2011
pete <(E-Mail Removed)> 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.
 
Reply With Quote
 
James Waldby
Guest
Posts: n/a
 
      03-10-2011
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
 
Reply With Quote
 
Tobias Blass
Guest
Posts: n/a
 
      03-11-2011


On Fri, 11 Mar 2011, pete wrote:

>Ben Bacarisse wrote:
>>
>> pete <(E-Mail Removed)> 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.
 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      03-11-2011
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
(E-Mail Removed)d
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
fflush(stdin), scanf and a space Tagore C Programming 5 12-29-2008 10:43 PM
difference between scanf("%i") and scanf("%d") ??? perhaps bug inVS2005? =?ISO-8859-1?Q?Martin_J=F8rgensen?= C Programming 18 05-02-2006 10:53 AM
scanf (yes/no) - doesn't work + deprecation errors scanf, fopen etc. =?ISO-8859-1?Q?Martin_J=F8rgensen?= C Programming 185 04-03-2006 02:49 PM
can I use scanf to get input (some times user enters input sometimes not, just hit keyboard)? santa19992000@yahoo.com C Programming 4 09-09-2005 03:38 AM



Advertisments