Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
572 views
in Technique[技术] by (71.8m points)

c - Detecting integral overflow with scanf

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...