When recently answering another question, I discovered a problem with code like:
int n;
scanf ("%d", &n);
With strtol
, you can detect overflow because, in that case, the maximum value allowed is inserted into n
and errno
is set to indicate the overflow, as per C11 7.22.1.4 The strtol, strtoll, strtoul, and strtoull functions /8
:
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.
However, in the sections of the standard dealing with scanf
, specifically C11 7.21.6.2 The fscanf function /10
, we see:
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.
Now, to me, that means any value can be returned and there's no mention of errno
being set to anything. This came to light because the asker of the linked question above was entering 9,999,999,999
into a 32-bit int
and getting back 1,410,065,407
, a value 233
too small, indicating it had simply wrapped around at the limit of the type.
When I tried it, I got back 2,147,483,647
, the largest possible 32-bit unsigned value.
So my question is as follows. How do you detect integral overflow in a portable way when using the scanf
family of functions? Is it even possible?
Now I should mention that, on my system (Debian 7), errno
is actually set to ERANGE
in these circumstances but I can find nothing in the standard that mandates this. Additionally, the return value from scanf
is 1
, indicating success in scanning the item.
The only portable way is to specify a field width, e.g. with "%4d"
(guaranteed to even fit into a 16-bit int
) or by building up the format string at run-time with a field width of (int)(log(INT_MAX) / log(10))
. This of course also rejects for example 32000, although it would fit into a 16-bit int
. So no, there is no satisfying portable way.
POSIX don't specify more here, nor mention ERANGE
.
This manpage mentions setting errno
only in case EOF
is returned; the glibc documentation doesn't mention ERANGE
at all.
That leaves the question what to suggest to beginners for reading integers, where I have no idea. scanf
has too many undefined and underspecified aspects to be really useful, fgets
cannot be used in productive code because you cannot handle 0-bytes properly, and portable error checking with strtol
and friends takes more lines than implementing the functionality yourself (and is quite easy to get wrong). The behaviour of atoi
is also undefined for integer overflow.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With