Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scanf won't execute for second time

Tags:

c

goto

scanf

I am trying:- To re-read the value if user enters an invalid value. But the problem is scanf() executes only once and won't execute any other time and programs gets stuck with an infinite loop.

#include<stdio.h>
#include<math.h>
main()
{
    unsigned int a;
    unsigned int b = pow(2,M-1);
    unsigned int c;
    int x;

    printf("b = %i",b);

    input:
    fflush(stdin);
    fflush(stdout);
    printf("\nEnter any integer: ");
    x = scanf("%u",&a);

    printf("%u",a);
    if(x==0) 
        goto input;

    printf("\na = %i",a);

    c = a & b;

    printf("\nc = %i",c);

    if(c)
        printf("\nthe bit %i is set",M);
    else
        printf("\nthe bit %i is not set",M);
}

I've tried using adding space before %u and also tried fflush(stdin) but nothing worked.

EDIT: I know that the use of goto is not recommended but I have to do it this way. (Using loop is not an option). M is a macro which I define using the gcc command line at compile time.

like image 221
sid-m Avatar asked Jul 24 '13 07:07

sid-m


2 Answers

Caution: fflush(stdin); may be undefined behavior. Read: Why fflush(stdin) is wrong?

int fflush(FILE *ostream);
The ostream points to an output stream or an update stream in which the most recent operation was not input, the fflush function causes any unwritten data for that stream to be delivered to the host environment to be written to the file; otherwise, the behavior is Undefined.

You can try a loop and read until EOF or \n given this FAQ entry instead of fflush(stdin) as I have suggested below in my answer.

Edit: thanks to @Jonathan Leffler:

There are platforms where fflush(stdin) is fully defined (as a non-standard extension on that platform). The primary example is a well-known family of systems known collectively as Windows. Microsoft's specification of int fflush( FILE *stream ); If the stream is open for input, fflush clears the contents of the buffer.

I have additional doubt in your code; what is M in expression unsigned int b = pow(2,M-1);? It should be an error if you don't define it. Are you posting complete code?

Regarding you logic of error detection:

re-read the value if user enters an invalid

No, scanf() does not return an error code. It returns the number of successful conversions.

int scanf ( const char * format, ... );
Return Value
On success, the function returns the number of items of the argument list successfully filled. This count can match the expected number of items or be less (even zero) due to a matching failure, a reading error, or the reach of the end-of-file.

If a reading error happens or the end-of-file is reached while reading, the proper indicator is set (feof or ferror). And, if either happens before any data could be successfully read, EOF is returned.

If an encoding error happens interpreting wide characters, the function sets errno to EILSEQ.

So actually depending on the error encountered the return value may be zero, EOF. You should use int ferror ( FILE * stream ); and errno macro for error detection (check the example given at link).

Errors possible because of invalid input can be:

EILSEQ: Input byte sequence does not form a valid character.
EINVAL: Not enough arguments; or format is NULL.
ERANGE: An integer conversion would exceed the size that can be stored in the corresponding integer type.

Check scanf manual for complete list.

Reason for infinite loop:

The system keeps track of which input has been seen so far. Every call to scanf picks up from where the last one stopped matching input. This means that if an error occurred with the previous scanf, the input it failed to match is still left unread, as if the user typed ahead. If care isn't taken to discard error input, and a loop is used to read the input, your program can get caught in an infinite loop.

So for example in your code:

x = scanf("%u", &a);
      //   ^
      //  need a number to be input

But suppose you don't input a number but an invalid string is entered e.g. "name" (instead of a number, as you say). This will cause the scanf() function to be fail when attempting to match an unsigned integer ("%u"), and the word "name" is left unread. So the next time through the loop, the scanf() doesn't wait for fresh user input, it tries to convert "name" again.

Similarly if the input were 29.67, the "%u" will match the first two characters only (the 29), leaving the .67 as unread input for the next call to scanf().

Even if the input is correct, as 29, the newline that ended the input is still left unread. Normally that isn't a problem since most conversions automatically skip leading white-space such as the trailing newline from the previous line. However some conversions ("%c" and "%[") don't skip any leading white-space, so you have to do it manually.

To avoid this infinite-loop One suggestion:

(remember: as I recommended the use of ferror(), error-value is preferable to detect invalid input. Additionally, it's just for learning purpose and if you need to implement a serious application you should use fgets(str) instead of scanf() followed by that parse str input to verify whether input is valid)

input:
    //fflush(stdout); //use if needed, as \n used in printf no need of fflush-stdout
    printf("\nEnter any integer: ");
    x = scanf("%u", &a); // always wait for new symbols
    printf("%u", a);

    if(x == 0){ // x=0, if error occurred
        // read all unread chars 
        while ((ch = getchar()) != '\n' && ch != EOF);
        goto input;
    }

It's just a suggestion that will possibly work with your code (worked for me, your code + gcc). But if you use this technique incorrectly it may leave a bug in your code:

Read How do I flush the input buffer?

If you are sure that unwanted data is in the input stream, you can use some of the following code snippets to remove them. However, if you call these when there is no data in the input stream, the program will wait until there is, which gives you undesirable results.

like image 154
Grijesh Chauhan Avatar answered Sep 24 '22 03:09

Grijesh Chauhan


This is a workaround to avoid infinite loop. Instead of goto please use do while loop:

do {
     printf("\nEnter any integer: ");
     x = scanf("%u",&a);

     printf("%u",a);
     if( x == 0)
     {
          char c;
          printf("hit any key \n");
          c = getchar();
     }

   } while(x==0);
like image 21
stev Avatar answered Sep 24 '22 03:09

stev