【问题标题】:What can I assume about the behaviour of atoi() on error?我可以假设 atoi() 错误时的行为是什么?
【发布时间】:2016-11-18 11:45:34
【问题描述】:

标准 C 库函数 atoi 在 ISO 9899:2011 中记录为:

7.22.1 数值转换函数

1 函数atofatoiatolatoll在出错时不需要影响整数表达式errno的值。如果结果的值无法表示,则行为未定义。

...

7.22.1.2 atoiatolatoll 函数

概要

#include <stdlib.h>
int atoi(const char *nptr);
long int atol(const char *nptr);
long long int atoll(const char *nptr);

说明

2 atoiatolatoll 函数将nptr 指向的字符串的初始部分分别转换为intlong intlong long int 表示。除了错误的行为,它们等价于

atoi: (int)strtol(nptr, (char **)NULL, 10)
atol: strtol(nptr, (char **)NULL, 10)
atoll: strtoll(nptr, (char **)NULL, 10)

返回

3 atoiatolatoll 函数返回转换后的值。

nptr 指向的字符串不能被解析为整数时,预期的行为是什么?似乎存在以下四种观点:

  • 不执行任何转换并返回零。这是 this one 等一些参考文献给出的文档。

  • 行为类似于strtol,只是errno 可能没有设置。这源于将“错误行为除外”作为对 §7.22.1 ¶1 的引用。

  • 行为未指定。这就是POSIX says

    调用 atoi(str) 应相当于:

    (int) strtol(str, (char **)NULL, 10)
    

    除了错误的处理可能不同。如果值无法表示,则行为未定义。

    此外,应用程序使用部分指出:

    atoi() 函数被 strtol() 包含,但由于它在现有代码中广泛使用而被保留。如果不知道该数字在范围内,则应使用 strtol(),因为 atoi() 不需要执行任何错误检查。

    请注意,POSIX 声称该规范符合 ISO 9899:1999(就我而言,它包含与 ISO 9899:2011 相同的语言):

    此参考页面上描述的功能符合 ISO C 标准。此处描述的要求与 ISO C 标准之间的任何冲突都是无意的。本卷 POSIX.1-2008 遵循 ISO C 标准。

    据我当地的 POSIX 委员会成员说,这是 UNIX 的历史行为。

  • 行为未定义。之所以出现这种解释,是因为 §7.22.1.2 ¶2 从未明确说明错误会发生什么。既没有定义也没有明确实现定义或未指定的行为是未定义的。

这些解释中哪一个是正确的?请尽量参考权威文档。

【问题讨论】:

  • If the value of the result cannot be represented, the behavior is undefined.
  • @cpplearner 当您尝试解析的数字超出结果类型的范围时,这句话适用。它不适用于输入根本无法解析的情况,此后不存在任何值。
  • 如果“错误行为”包括没有数字要转换时发生的情况,那么它是未定义的。如果这不是“错误”,则返回值为 0。但如果知道精确的行为如此重要,为什么不直接使用文档更完善的函数 strtol 代替呢? (或者如果您希望返回类型为int 而不是long int,则将strtol 包装在您自己的函数中?)
  • @DavidK 因为我想知道答案。

标签: c language-lawyer undefined-behavior atoi


【解决方案1】:

nptr 指向的字符串无法解析为整数时的预期行为是什么?

要明确,这个问题适用于

// Case 1
value = atoi("");
value = atoi("  ");
value = atoi("wxyz");

而不是以下:

// Case 2
// NULL does not point to a string
value = atoi(NULL);
// Convert the initial portion, yet has following junk
value = atoi("123xyz");
value = atoi("123 ");

根据整数的使用情况,可能/可能不是以下内容。

// Case 3
// Can be parsed as an _integer_, yet overflows an `int`.
value = atoi("12345678901234567890123456789012345678901234567890");

ato*()的“非Case 2”行为取决于

error的含义

atoiatolatoll 函数将nptr 指向的字符串的初始部分分别转换为intlong intlong long int 表示。除了error上的行为外,它们等价于
atoi: (int)strtol(nptr, (char **)NULL, 10)
...
C11dr §7.22.1.2 2


当然错误包括案例3:“如果正确的值超出了可表示的值的范围”。 strto*(),虽然可能不是ato*(),但在这种情况下确实设置了errrno&lt;errno.h&gt; 中定义的错误 数字。由于ato*() 的规范不适用于此错误,因此溢出,结果是UB per

未定义的行为在本国际标准中以“未定义的行为”一词或省略任何明确的行为定义来表示。 C11dr §4 2


对于情况 1,strto*() 的行为已明确定义,未指定影响errno。规范进行了详细介绍(第 7.22.1.4 4 节)并将这些称为“无转换”,而不是 错误。因此它可以断言 case 1 strto*() 行为不是错误,而是“无转换”。因此每...

"如果无法执行转换,则返回零。C11dr §7.22.1.4 8

...atoi("") 必须返回 0。

【讨论】:

  • 有趣的解释。
  • @FUZxxl 本来打算补充的,但没有意见:如有疑问,请考虑历史。编写 C89 是为了避免废弃大部分代码库。随后的版本非常缓慢地收紧了行为定义。 C89 之前的atoi("") 代码是否会返回 0 以外的内容或出现错误?是的,我想是这样。我非常怀疑 2016 年以后使用的任何编译器是否会执行返回 0 以外的操作。我怀疑这里的规范足够模糊,允许所有早期的atoi() 代码,但规范被调整为暗示:“当然,“no_conversion”应该为atoi() 返回 0”。 IAC,最好使用strto*()
  • @chux:从 1989 年到大约 2000 年,可能会做任何奇怪事情的实现比例会下降到几乎没有。然而,从那时起,以 UB 为借口将时间定律和因果律抛到一边变得很流行,所以除非或直到有一个标准的 C 语言核心方言,否则需要防止奇怪的代码量行为会增加。
  • @supercat 同意。 ato*()/strto*() 的“核心方言”会有所帮助。规范中没有 UB 的东西。
  • ...暗示高质量的实现应该定义行为;然而,无论出于何种原因,该标准的作者似乎都故意避免过多谈论高质量实现的期望,并且由于上述行为分类与 UB 之间的唯一区别是将某些实现视为劣等,因此该标准的作者认为没有必要进行这种区分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-05
相关资源
最近更新 更多