【发布时间】:2015-03-16 10:52:48
【问题描述】:
最近answering another question的时候,发现代码有问题:
int n;
scanf ("%d", &n);
使用strtol,您可以检测溢出,因为在这种情况下,允许的最大值被插入到n,并且errno 被设置为指示溢出,根据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中。
但是,在处理scanf,特别是C11 7.21.6.2 The fscanf function /10 的标准部分中,我们看到:
如果此对象没有适当的类型,或者转换的结果无法在对象中表示,则行为未定义。
现在,对我来说,这意味着可以返回 any 值,并且没有提到将 errno 设置为任何值。这是因为上面链接问题的提问者将9,999,999,999 输入到32 位int 并返回1,410,065,407,值2<sup>33</sup> 太小,表明它只是简单地绕在了类型。
我尝试了一下,我得到了2,147,483,647,这是最大可能的 32 位无符号值。
所以我的问题如下。使用scanf 系列函数时,如何以可移植的方式检测积分溢出?有没有可能?
现在我应该提一下,在我的系统 (Debian 7) 上,errno is 在这些情况下实际上设置为 ERANGE,但我在 标准中找不到任何内容 em> 要求这样做。另外scanf的返回值为1,表示扫描成功。
【问题讨论】:
-
我可以看到
errno在这里不能正常工作的一个原因:scanf可以进行各种转换。errno适用于哪一个? (如果设置errno等同于转换失败,那将是明确的,但显然情况并非如此,即使在strtol中也是如此。) -
你的系统是什么?你说在我的系统上
errno... -
@iharob:Debian 7,我会更新这个问题,但我并不是真正追求特定于实现的东西,而是根据标准的“犹太洁食”。
-
您应该做的第一件事是检查扫描的返回值——它返回成功转换的数量。在你的例子中 if (scanf (..) != 1) 你有一个错误。
-
深入研究这件事有什么意义? stdio.h 和 scanf 一直只适用于非常基本的输入。虽然 scanf 可能适用于课堂或简单的数字运算应用程序,但除此之外,您不能安全地依赖它。如果您想保护用户不输入错误的数据、控件或函数序列,您必须考虑编写自己的库并从输入缓冲区中获取数据。
标签: c language-lawyer iso