Formatted input functions such as scanf()
, fscanf()
, vscanf()
, and vfscanf()
can be used to read string data from stdin
or (in the cases of fscanf()
and vfscanf()
) other input stream. These functions work fine for valid integer values but lack robust error handling for invalid values.
Instead of these functions, try inputing the value as a string and then converting it to an integer valoue using strtol()
or a related function INT00-A.
Non-Compliant Example 1
This non-compliant code example converts the string stored in the static array buff
to a signed integer value using the atoi()
function.
char buff [25]; int int_var; fgets(buff, sizeof buff, stdin); int_var = atoi(buff);
The atoi()
, atol()
, and atoll()
functions convert the initial portion of astring pointed to int
, long int
, and long long int
representation, respectively. Except for the behavior on error, they are equivalent to
atoi: (int)strtol(nptr, (char **)NULL, 10) atol: strtol(nptr, (char **)NULL, 10) atoll: strtoll(nptr, (char **)NULL, 10)
Unfortunately, atoi()
and related functions lack a mechanism for reporting errors for invalid values. Specifically, the atoi()
, atol()
, and atoll()
functions:
- do not need to set errno on an error
- have undefined behavior if the value of the result cannot be represented
Non-Compliant Example 2
This non-compliant example uses the sscanf()
function to convert a string to an integer. The sscanf()
function has the same problems as atoi()
.
char buff [25]; int int_var; fgets(buff, sizeof buff, stdin); sscanf("%d", buff, &int_var);
Compliant Solution
The following compliant example uses strtol()
to input an integer value and provides error checking to make sure that the value is a valid integer in the range of int
.
char buff [25]; char *end_ptr; long long_var; int int_var; fgets(buff, sizeof buff, stdin); errno = 0; long_var = strtol(buff, &end_ptr, 0); if (ERANGE == errno) { puts("number out of range\n"); } else if (long_var > INT_MAX) { printf("%ld too large!\n", long_var); } else if (long_var < INT_MIN) { printf("%ld too small!\n", long_var); } else if (end_ptr == buff) { printf("not valid numeric input\n"); } else { int_var = (int)long_var; }
If you are attempting to convert a string to a smaller interger type (int
, short
, or signed char
), then you only need test the result against the limits for that type. The tests do nothing if the smaller type happens to have the same size and representation on a particular compiler.
References
- Jack Klein. Bullet Proof Integer Input Using strtol(). http://home.att.net/~jackklein/c/code/strtol.html
- ISO/IEC 9899-1999 Section 7.20.1.4 The strtol, strtoll, strtoul, and strtoull functions; Section 7.20.1.2 The atoi, atol, and atoll functions; 7.19.6.7 The sscanf function