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 gcc, clang and msvc.
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
}
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
, andstrtoull
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
, orULLONG_MAX
is returned (according to the return type and sign of the value, if any), and the value of the macroERANGE
is stored inerrno
.
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;
}