0

Yes, there are already similar issues on SO on that scanf() does not wait for the user's input before "interpreting" an enter keypress, but my problem is that this problem only occurs when the scan -- which is %ld (with a space in front) -- reads a string instead of another long number. Details are below if this summary seems convoluted.

I have the following program in C:

#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    long int cardnum, tmp;
    int num_of_digits;
    bool validate; // valid value
    LOOP:do
    {
        printf("Number: ");
        scanf(" %ld", &cardnum);

        tmp = cardnum, num_of_digits = 0;
        while (tmp != 0)
        {
            tmp /= 10;
            ++num_of_digits;
        }

        if (num_of_digits != 16)
        {
            printf("INVALID\n");
            goto LOOP;
        }

        validate = false;
        // ... (validate will be processed here, and there will be a case where "validate" will be true)
    }
    while (validate != true);
}

Inputs:

  • 4003600000000014: works, length is 16
  • 400360000000001: prints INVALID, length is 15
  • asdf: infinite loop (notice how not all invalid inputs will result in this bug!)
Number: INVALID
Number: INVALID
Number: INVALID
...

I have read elsewhere that scanf ignores the last line buffer (or something) but in my case, it only happens when scanf did not receive the right input type from the user. Can anyone please help?

Thanks in advance.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
crimsonpython24
  • 2,223
  • 2
  • 11
  • 27
  • So what do you _want_ to happen on `asdf`? `goto LOOP;` that is horrible and do not use goto. Just `continue`. – KamilCuk Jun 09 '21 at 11:41
  • That space before the format specifier never worked for me, so it's getting stuck on the newline in stdin, easier thing to do is use `getchar()` to get it out of there, better thing to do is read it with `fgets` and convert it to an `int` – LEF Jun 09 '21 at 11:41
  • @KamilCuk if `asdf` was entered I'd like to have the user input again. The purpose of the `goto` was to not enter the processes below for optimization purposes. If there's a function that does the same but is "better," please let me know – crimsonpython24 Jun 09 '21 at 11:42
  • What should I use instead then? But the error described in the problem persists even if I used `continue`. – crimsonpython24 Jun 09 '21 at 11:45
  • 3
    @LEF, a literal space character in the format string matches however much whitespace appears at the corresponding point in the input, including none. However, *most* conversion specifiers, including `%d`, ignore any leading whitespace anyway, so format `" %ld"` matches exactly the same inputs that `"%ld"` does. – John Bollinger Jun 09 '21 at 11:52

2 Answers2

2

how not all invalid inputs will result in this bug!)

If your input is not a number, then scanf will fail. So check if it fails to scan a number and if the scanning failed, then ignore the input until the end of line, for example. Note - you should handle EOF too.

if (scanf(...) != 1) {
    int c;
    // ignore input
    while ((c = getchar()) != EOF) {
        // until a newline!
        if (c == '\n') {
            break;
        }
    }
    // when eof
    if (c == EOF) {
        // then exit our program with a failure!
        printf("ERROR: End of input!");
        exit(-1);
    }
    // if not eof, means user is ready to enter yet another line
    printf("you inputted an invalid line - please input a line consisting of digits only!");
    continue;
}

See return value description in cppreference scanf.


LOOP:do { and goto LOOP; looks like a horrible idea - do not use goto there, seems confusing. Instead prefer to restructure your code so that you do not have to use goto. For example:

for (;;) {
    if (some_check) {
         continue;
    }
    if (some_another_check) {
         continue;
    }
    // if we get here, means all checks succeeded - so we need to break
    break;
}

There are nice uses of goto and it happens to be used - see ex. https://www.kernel.org/doc/html/v4.10/process/coding-style.html#centralized-exiting-of-functions .

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
1

If you entered something other than an integer number then you need to remove from the input buffer the invalid input.

Here is a demonstrative program.

#include <stdio.h>

int main(void) 
{
    long int x;
    int valid;
    
    do
    {
        if ( !( valid = ( scanf( " %ld", &x ) == 1 ) ) && valid != EOF )
        {
            scanf( "%*[^\n]%*c");
        }
    } while ( !valid );
    
    printf( "x = %ld\n", x );
    
    return 0;
}

The console output might look like

A
12
x = 12

Pay attention to that using the goto statement is a bad programming style. Instead you could use do-while statement as it is shown in my program. That is in fact the goto statement in your program is redundant.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335