atoi() — 字符串转整数

12

我看到atoi()已经被弃用了,它的等价函数是:

(int)strtol(token_start, (char **)NULL, 10);

这是否意味着我应该使用上述方法而不是atoi(chr),还是说它只是在说它们是等效的?


3
我对atoi()的问题是:assert( atoi("0") != atoi("!blah") ) - sbi
3
atoi很差,因为它几乎没有定义良好的错误检测,但即使使用strtol,正确地检测(和分类)错误也令人惊讶地困难,并且这里没有任何答案真正解决了这个问题。请参阅此问题中的答案以获得一些指导。 - Steve Summit
6个回答

22

atoi没有被弃用,您的源代码是错误的。当前C标准ISO 9899:2011中没有任何指示(例如,请参见第6.11章“未来语言方向”),也没有较早的标准。

根据C标准,atoi等效于strtol,如下所示,C11 7.22.1.2:

atoi、atol和atoll函数将由nptr指向的字符串的初始部分转换为int、long int和long long int表示形式。

除了出现错误行为外,它们与

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

atol:strtol(nptr,(char **)NULL,10)

atoll:strtoll(nptr,(char **)NULL,10)

优先选择strtol,因为在出现错误时,atoi会引起未定义的行为。请参见7.22.1:“如果无法表示结果的值,则行为是未定义的。”


3
(是的,我知道这个问题非常老了,但接受的答案是不正确的,必须澄清) - Lundin
2
如果结果的值无法表示,则行为未定义。这肯定使函数完全无法使用,并足以考虑将该函数视为已弃用。 - William Pursell
1
@WilliamPursell 的确。再给C标准委员会几十年,他们可能会意识到。例如,他们花了22年时间才删除了gets。这是效率和主动性的典范。与此相比,MISRA-C安全子集标准在1998年就禁止了所有ato*函数的使用。 - Lundin

8

在Apple的Mac OS X手册页(以及BSD手册页)中,确实说明了atoi已经被弃用。

atoi()函数已被strtol()取代,不应在新代码中使用。

出于这个原因,我会使用strtol()函数的等价物,但我怀疑您不必担心atoi()被移除的问题。

来自http://www.codecogs.com/library/computing/c/stdlib.h/atoi.php的实现注释

* The atoi function is not thread-safe and also not async-cancel safe.
* The atoi function has been deprecated by strtol and should not be used in new code.


3
atoi函数是否是线程安全的,仅取决于它的内部实现吗?如果不是,那是什么导致了它在外部不是线程安全的? - SasQ
4
他们一定尽力使其不具备线程安全性,但朴素的实现方式肯定是具备线程安全性的。 - Per Johansson
需要注意的是,并非所有编译器都支持sto*函数,例如Android NDK。请参考此问题获取更多信息:https://dev59.com/vG_Xa4cB1Zd3GeqP5thL - catalyst294
1
glibc中atoi的man页面说:“atoi(),atol()和atoll()函数是线程安全的,但有例外。只要在它们的执行期间没有调用setlocale(3)来更改区域设置,这些函数就可以在多线程应用程序中安全地使用。” - dequis
2
这个答案或者说源代码是不正确/不相关的。某个函数是否线程安全确实取决于编译器的实现。我不明白为什么它不会是线程安全的。在这方面,atoi与strol函数没有区别。更重要的是,atoi绝对不是被弃用的。在2009年和C11标准中都没有被弃用。 - Lundin
@Lundin 我无法删除这个答案,因为它是被接受的,所以我进行了更新。我发现BSD和OSX的手册页面确实说它已经被弃用了,尽管我不知道原来的问题指的是什么。 - John Boker

4
atoi()的描述与strtol()的相似性和差异有一个非常重要的点。

>...调用atoi(str)等同于:
>(int) strtol(str, (char **)NULL, 10)
>除了错误处理可能不同。
const char *buf = "forty two";
int t1 = atoi(buf);             /* detect errors? */
int t2 = strtol(buf, NULL, 10); /* detect errors? */


我已经在我的编译器(GCC)上尝试了你的代码,两者都给了我0,所以我没有看到任何区别 :| - SasQ
嗯,我的错!谢谢 @SasQ。我以为 strtol 在出错时必须设置 errno,但在我上面的测试代码的特定情况下,它并没有这样做。 - pmg
@pmg,你的测试代码似乎根本没有检查errno - Dan Bechard

3
不,你不应该使用上述代码代替atoi
事实上,你应该检查strtol提供的错误信息:
i = atoi(s);

应该被替换为

char* stopped;
i = (int)strtol(s, &stopped, 10);
if (*stopped) { /* handle error */ }

0
  • atoi()在之前或当前的C标准中都没有被弃用。

  • atoi(nptr)(int)strtol(nptr, (char **)NULL, 10);相似,但在出错时有所不同


现在,如何检测和解决错误呢?
考虑使用一个辅助函数:int atoi_alt(const char *nptr, int *errorptr);
可能的错误和候选解决方案:
超出范围:转换导致的值超出了[INT_MIN ... INT_MAX]范围,例如atoi_alt("1234567890123456789012345678901234567890", ...);。返回与原始转换符号相匹配的INT_MIN或INT_MAX,并返回非零错误。
无法转换:由于字符串以非数字、非空白字符开头,例如atoi_alt("!@#$1234", ...);。返回0和非零错误。
无法转换:由于字符串为空,例如atoi_alt("", ...);。返回0和非零错误。
非数字尾随文本:初始转换成功,但存在额外的非空白字符文本,例如atoi_alt("1234!@#$", ...);。返回转换后的值和非零错误。请注意,使用strtol()时,这不是一个错误。
空白字符尾随文本:初始转换成功,后面跟着空白字符,例如atoi_alt("1234\n", ...);。返回转换后的值和零错误。请注意,使用strtol()时,这不是一个错误。
空白字符开头文本:忽略前导空白字符并按上述方式进行转换。请注意,这与atoi()和strtol()一致,并且本身不是一个错误。
未提供字符串:由于nptr == NULL,返回0和非零错误。请注意,使用strtol()时,这是未定义行为(UB)。
未提供错误地址:执行与上述相同的操作,但不设置错误。
示例备用代码:
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>

// To Do: refine error codes, perhaps as enum?
#define ATOI_BAD_ARG 1
#define ATOI_NO_CONVERSION 2
#define ATOI_RANGE 3
#define ATOI_EXTRA 4
int atoi_alt(const char *nptr, int *errorptr);

// Side effect: errno is assigned.
int atoi_alt(const char *nptr, int *errorptr) {
  int dummy;
  if (errorptr == NULL) {
    errorptr = &dummy;
  }
  if (nptr == NULL) {
    *errorptr = ATOI_BAD_ARG;
    return 0;
  }

  errno = 0;
  char *end;
  long lvalue = strtol(nptr, &end, 10);
  if (nptr == end) {
    *errorptr = ATOI_NO_CONVERSION;
    return 0;
  }

#if LONG_MIN == INT_MIN && LONG_MAX == INT_MAX
  if (errno == ERANGE) {
    *errorptr = ATOI_RANGE;
    return (int) lvalue;
  }
#else
  if (errno == ERANGE || lvalue < INT_MIN || lvalue > INT_MAX) {
    errno = ERANGE;
    *errorptr = ATOI_RANGE;
    return (lvalue < 0) ? INT_MIN : INT_MAX;
  }
#endif

  while (isspace(((unsigned char* ) end)[0])) {
    end++;
  }

  *errorptr = *end ? ATOI_EXTRA : 0;
  return (int) lvalue;
}

代码故意不通过errno将错误信息传递回调用者 - 这是我的设计选择。很容易修改代码以做出其他选择。

-3

这意味着在某个时间点上,atoi将不再可用。所以现在开始改变你的代码吧。


我理解了那部分,但想知道 atoi 的实现是否只调用了上面的代码。 - user105033
6
这是错误的,需要引用证明。目前的C标准中没有显示atoi已被弃用。此外,这个回答发布已经6年了,现在活跃的标准是C11。 - Lundin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接