Home > OS >  Scanf() not able to detect wrong input
Scanf() not able to detect wrong input

Time:06-30

int i,f;
f = scanf("%d",&i);

When I enter input as 3333333333333333333333(greater than the capacity of int). Shouldnt the value of f be 0?

CodePudding user response:

No, it can't be detected that way.

The below is not a portable solution, but it works in the latest releases of , and .

You need to set errno = 0; first and then check it for range errors:

#include <errno.h>

// ...

    errno = 0;
    f = scanf("%d",&i);
    if(f == 1 && errno != ERANGE) {
        // success
    }

Demo

For portability, read this from an early draft of the C2x standard:

Unless assignment suppression was indicated by a *, the result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a conversion result. If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

A better (as in portable) option to detect this would be to read into a char[] buffer first and then use strtol() to convert it to a number. From the same standard draft:

The strtol, strtoll, strtoul, and strtoull functions return the converted value, if any. If no conversion could be performed, zero is returned. If the correct value is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type and sign of the value, if any), and the value of the macro ERANGE is stored in errno.

Here's a demonstrative program using strtol:

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

#define Size(x) (sizeof (x) / sizeof *(x))

int main() {
    const char* strings[] = {
        "3333333333333333333333 foo",
        "2147483647 will probably succeed",
        "2147483648 will probably fail",
        "32767 guaranteed success"
    };

    char *end; // this will point at where the conversion ended in the string

    for(unsigned si = 0; si < Size(strings);   si) {

        errno = 0; // clear it from any previous error (must be done)
        long result = strtol(strings[si], &end, 10);

        if(errno == ERANGE) {
            perror("to big for a long");
        } else if(result < INT_MIN || result > INT_MAX) {
            fprintf(stderr, "to big for an int\n");
        } else {
            int i = result;       // here it's safe to initialize an int
            printf("%d %d rest=[%s]\n", errno, i, end);
        }
    }
}

Possible output:

to big for a long: Numerical result out of range
0 2147483647 rest=[ will probably succeed]
to big for an int
0 32767 rest=[ guaranteed success]

CodePudding user response:

Shouldnt the value of f be 0?

With standard C, no. With scanf("%d",&i), on int overflow, the result is undefined.

With scanf() in Unix (of which there are variations), I find no prevention of undefined behavior with overflow.

Best to ditch (not use) scanf() and use fgets() for all user input.


Code could try a textual width limit and a wider type:

intmax_t bigd;
//          vv --- width limit
if (scanf("jd",&bigd) == 1 && bigd >= INT_MIN && bigd <= INT_MAX) {
  d = (int) bigd;
} else {
  puts("Oops");
}

Yet that has trouble on novel implementations where int is as wide as intmax_t.


scanf() returns 0 when no int textual input found.

A key design element missing from OP's questions is what should happen to user input that exceeds the int range? Stop reading after the first `"333333333"?

What is best, depends on how OP wants to handle, in detail, error conditions - something not yet stated.

CodePudding user response:

scanf("%d", &i) does not detect overflow, worse even, scanf() has undefined behavior if the number exceeds the range of the destination type: depending on the implementation, the value of i could be -434809515, -1, INT_MAX or any value including a trap value with or without some more exotic side effect.

The proper way to check the input is to read it as a line in an array of char and to parse it with strtol():

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    char input[120];
    char ch;
    char *p;    
    long x;
    int i;
    int last_errno;

    printf("Enter an integer: ");
    if (!fgets(input, sizeof input, stdin)) {
        fprintf(stderr, "missing input\n");
        return 1;
    }
    errno = 0;
    x = strtol(input, &p, 0);
    last_errno = errno;
    if (p == input || sscanf(p, " %c", &ch) == 1) {
        fprintf(stderr, "invalid input: %s", input);
        return 1;
    }
    if (last_errno != 0 || x < INT_MIN || x > INT_MAX) {
        fprintf(stderr, "number too large: %s", input);
        return 1;
    }
    i = (int)x;  // we know `x` is in the proper range for this conversion
    printf("The number is %d\n", i); 
    return 0;
}
  • Related