1

I'm relatively new to C and would like to know how to prevent an overflow from input...

So for example, I have:

scanf("%d", &a);

Where a is an integer.

So what I could I do to prevent someone from entering a number that's larger than max integer? Due to the constraints of the problem I'm working on, you HAVE to use scanf. How do I go about restricting the input?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
user3290727
  • 11
  • 1
  • 2
  • Use `scanf` to read a string and then parse it yourself ? – Paul R Feb 09 '14 at 21:45
  • 2
    Theoretically you can see if errno is ERANGE for an out-of-range error. However this is not standard specified for scanf. See http://linux.die.net/man/3/scanf `''C89 and C99 and POSIX.1-2001 standards do not specify the ERANGE error ''` In other words, you should use `strtol` if you need to read an integer while also having the ability to check for overflow. – Brandin Feb 09 '14 at 23:05
  • If you *must* use scanf, one simple way would be to read a `double` instead using scanf (which has a larger range), do your own range check, and then cast the result to an `int` if it meets your criteria. This will work without loss of information so long as your valid integer range is not too large. – Brandin Feb 09 '14 at 23:07
  • @Brandin: The ANSI-C spec does say that a sequence of digits is matched and then converted with the strtol function. So if the value is too large for a long, you will get an ERANGE. However, it is unclear what happens if it is too large for an int but fits in a long. – Chris Dodd Feb 24 '21 at 02:25

1 Answers1

2

It is very challenging to prevent user input.
There is no magic hand to reach out and stop the user from beating away at the keyboard.

But code can limit what it reads.

  1. scanf() is tough to limit. It may not set errno on overflow. Code can limit the number of char to say 9. That's a first step but one can not enter values like "1000000000" or "00000000000000001".

    // Assume INT_MAX = 2,147,483,647.
    scanf("%9d", &a);
    
  2. A pedantic method would use fgetc(). An unsigned method follows. int takes a bit more.

    unsigned a = 0;
    int ch = fgetc(stdin);
    while (isspace(ch)) {
      ch = fgetc(stdin);
    }
    while (isdigit(ch)) {
      ch -= '0';
      if (a >= UINTMAX/10 && (a > UINTMAX/10 || ch > UINTMAX%10)) {
        a = UINTMAX;
        break;  // overflow detected.
      }
      a = a*10 + ch;
      ch = fgetc(stdin);
    }
    ungetc(ch, stdin);  // Put back `ch` as it was not used.
    
  3. But I prefer to change the goal and simply tell the user again, even if it means reading in more characters.

    // 0:success or EOF
    int Read_int(const char *prompt, int *dest, int min, int max) {
      for (;;) {
        char buf[(sizeof(int)*3 + 3)*2];  // 2x generous buffer
        fputs(prompt, stdout);
        if (fgets(buf, sizeof buf, stdin) == NULL) {
          return EOF;
        }
        char *endptr;
        errno = 0;
        long l = strtol(buf, &endptr, 10);
        // no conversion or junk at the end ....
        if (buf == endptr || *endptr != '\n') {
          continue;
        }
        if (!errno && l >= min && l <= max) {
         *dest = (int) l;
         return 0; // success
        }
      }
    }  
    
    // Sample usage
    int a;
    Read_int("Enter an `int`\n", &a, INT_MIN, INT_MAX);
    
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256