【问题标题】:Detecting integral overflow with scanf使用 scanf 检测积分溢出
【发布时间】: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


【解决方案1】:

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

POSIX此处不再赘述,也不要提及ERANGE

This manpage 仅在返回 EOF 的情况下提及设置 errnothe glibc documentation 根本没有提到 ERANGE

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2010-12-19
    • 2021-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多