0

In c I have this snippet

    int n=0; 
    do {
        printf("random text\n");
        scanf("%d", &n); 
    } while ( n < 1 || n > 10);

when the user inputs anything other than a number the loop spams the printf without waiting for user input

why does this happen and how do I fix this

  • 3
    Always a bug: not testing the return value from scanf. – Jens Jun 05 '22 at 19:51
  • 3
    You fix it by not using `scanf`, Use `fgets` and then apply `sscanf` or other string processing functions. – Weather Vane Jun 05 '22 at 20:04
  • @WeatherVane what if you can't use anything except for `scanf` – packet_sniffer Jun 05 '22 at 20:09
  • Who or what forces you to use `scanf`? It's a very limited function that causes more harm than good. It seems to be a teacher's favourite. Most teachers are not actually programmers though. – Cheatah Jun 05 '22 at 20:34
  • @Cheatah yes it is a requirement to use this function I know other functions but i am not allowed to use them – packet_sniffer Jun 05 '22 at 20:36
  • 1
    Well now you know why the function sucks. You could test for the return value. If it's not 1, have some more forgiving `scanf` format string eat up the input and try again. – Cheatah Jun 05 '22 at 20:42
  • 1
    Your instructor should not have restricted you to `scanf`, and you may tell them we said so. Perhaps refer them to https://stackoverflow.com/questions/58403537/what-can-i-use-for-input-conversion-instead-of-scanf – zwol Jun 05 '22 at 20:48
  • 1
    If the teacher is making you use `scanf` then you could spend an hour or two with a little test program and the man pages, particularly with a loop where more than one entry is needed. Note that understanding how different format specifiers handle whitespace in the input, and in the format string itself, is critical to using the function successfully, and without messy kludges. There are many pitfalls with that function family, it really is tricky and worth spending some time on. – Weather Vane Jun 05 '22 at 20:55
  • The problem mentioned in the question as well as the solution is mentioned in section 1 of the following guide: [A beginners' guide away from scanf()](http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html) – Andreas Wenzel Jun 05 '22 at 21:47

2 Answers2

3

When the user enters bad data, the un-converted data remains in the input buffer.

You then repeatedly attempt to convert that data to an integer, and fail each time, leading to infinite repetition.

You can clear the input buffer up to the next new-line in case of failure.

Personally, I generally prefer to read in a line to start with, then attempt to convert that with sscanf, on this general order:

char buffer[256];
do {
    fgets(buffer, sizeof(buffer), stdin);
} while (sscanf(buffer, "%d", &n) == 0 || n < 1 || n > 10);

Generally it's better to wrap the calls to fgets and sscanf up in a function though, so you can return some error indication when either one fails (since this doesn't check the return value from fgets it can/will continue trying to read, even if reading from the stream fails).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • how do I fix this – packet_sniffer Jun 05 '22 at 19:56
  • Maybe `sscanf(buffer, "%d", &n) != 1`? I am often concerned about `scanf()` returning `EOF, 0, 1,...` Since only `1` is the desired input, perhaps test against that. Pedantically, since overflow is UB with `"%d"`, maybe `"%4d"` or some input limitation? Still, answer in on the right track. – chux - Reinstate Monica Jun 05 '22 at 23:31
1

You need to skip the current content of the buffer if it does not contain a valid number. For example

int n=0; 
do {
    printf("random text\n");
    if ( scanf("%d", &n) == 0 )
    {
        scanf( "%*[^\n]" );
    }    
} while ( n < 1 || n > 10);

Pay attention to that this prompt

    printf("random text\n");

is confusing when you expect from the user to enter a number.

In general you should also add a check for EOF. For example

int result = 1;
int n=0; 
do {
    n = 0;
    printf("random text\n");

    result = scanf("%d", &n);

    if ( result == 0 )
    {
        scanf( "%*[^\n]" );
    }
    else if ( result == EOF )
    {
        break;
    }  
} while ( n < 1 || n > 10);

if ( result == EOF ) exit( 1 );
//...
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335