用scanf检测积分溢出 [英] Detecting integral overflow with scanf

查看:105
本文介绍了用scanf检测积分溢出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近回答另一个问题时,我发现了一个代码类似的问题:

When recently answering another question, I discovered a problem with code like:

int n;
scanf ("%d", &n);

使用strtol,您可以检测到溢出,因为在这种情况下,根据C11 7.22.1.4 The strtol, strtoll, strtoul, and strtoull functions /8,将允许的最大值插入n并将errno设置为指示溢出:

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:

如果正确值超出可表示值的范围,则返回LONG_MIN,LONG_MAX,LLONG_MIN,LLONG_MAX,ULONG_MAX或ULLONG_MAX(根据返回类型和值的符号,如果有的话),以及宏ERANGE存储在errno中.

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.

但是,在涉及scanf,特别是C11 7.21.6.2 The fscanf function /10的标准部分中,我们看到:

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.

现在,对我来说,这意味着可以返回 any 值,而且没有提到将errno设置为任何值.由于上面链接的问题的提问者正在将9,999,999,999输入到32位int中并返回1,410,065,407,该值233太小,表明它只是在的极限处缠绕类型.

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.

当我尝试使用 时,我又得到了2,147,483,647,它是最大的32位无符号值.

When I tried it, I got back 2,147,483,647, the largest possible 32-bit unsigned value.

所以我的问题如下.使用scanf系列功能时,如何以可移植的方式检测积分溢出?甚至有可能吗?

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?

现在我要提到的是,在我的系统(Debian 7)上,在这种情况下errno 实际上设置为ERANGE,但在标准中却找不到任何内容强制执行此操作.另外,scanf的返回值是1,表示扫描项目成功.

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.

推荐答案

唯一可移植的方法是指定字段宽度,例如使用"%4d"(保证甚至适合16位int)或通过在运行时建立字段宽度为(int)(log(INT_MAX) / log(10))的格式字符串来实现.这当然也拒绝了例如32000,尽管它可以放入16位的int中.因此,没有令人满意的可移植方式.

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 在此未指定更多内容,也未提及.

POSIX don't specify more here, nor mention ERANGE.

此联机帮助页仅在返回EOF的情况下提及设置errno glibc文档没有提及.

This manpage mentions setting errno only in case EOF is returned; the glibc documentation doesn't mention ERANGE at all.

剩下的问题是,我不知道该如何建议初学者阅读整数. scanf的未定义和未指定的方面太多,无法真正有用,fgets无法用于生产代码中,因为您无法正确处理0字节,并且与strtol和朋友一起进行的可移植错误检查比实现功能需要更多的行自己(而且很容易出错).对于整数溢出,atoi的行为也未定义.

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.

这篇关于用scanf检测积分溢出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆