【问题标题】:atoi — how to identify the difference between zero and error?atoi — 如何识别零和错误之间的区别?
【发布时间】:2012-01-15 17:25:18
【问题描述】:

http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/

返回值

成功时,函数将转换后的整数作为int 值返回。 如果无法执行有效转换,则返回零值。 如果正确值超出可表示值的范围,则返回 INT_MAX 或 INT_MIN。

那么我在atoi("poop")atoi("0")atoi("0000000") 之间有什么区别

是的,我可以循环并检查所有零,以防我得到 0 结果,但没有更好的方法吗?

注意:我使用的是 ANSI C89

【问题讨论】:

  • cplusplus.com is wrong about atoi,它根本不检测错误。不要相信那个网站。
  • 当正确值超出范围时,C89、C99 和 C11 标准对 atoi() 返回的值只字未提。 C++11 标准说得更少(它在两个表中列出了atoi,仅此而已!)。 cplusplus.com 的声明本质上是一厢情愿和/或常见的实现——没有任何标准保证。

标签: c atoi


【解决方案1】:

这就是atoi 有时被认为不安全的原因之一。请改用strtol / strtoul。如果你有它,请使用strtonum

函数atoi 比你想象的更危险。 The POSIX standard 说:

如果值无法表示,则行为未定义。

C99 标准也这样说:

7.20.1

atof、atoi、atol 和 atoll 函数不需要影响 整数表达式 errno 出现错误。如果结果的值 无法表示,行为未定义。

【讨论】:

  • 不回答标题问题。
  • @BeeOnRope 编辑队列已满。答案是:“它是未定义的,因此您无法可靠地确定差异”
【解决方案2】:

正如@cnicutar 和@ouah 所述,atoi 无法区分有效的 0 和无效的字符串,这使得 strtol 系列成为更好的选择。

但是Why?How?首先要了解atoistrtol都只将字符串中的初始数字集转换为数值。任何尾随的非数字字符都将被忽略。 strtol 可用于检查无效字符串,因为除了数值之外,它还返回指向字符串数字部分末尾的指针。因此,如果这个end 指针仍然指向原始字符串的开头,则可以判断存在错误并且字符串中没有任何字符被转换。

还有一些其他的细微之处,如代码示例所示:

long lnum;
int num;
char *end;

errno = 0;

lnum = strtol(in_str, &end, 10);        //10 specifies base-10
if (end == in_str)     //if no characters were converted these pointers are equal
    fprintf(stderr, "ERROR: can't convert string to number\n");

//If sizeof(int) == sizeof(long), we have to explicitly check for overflows
if ((lnum == LONG_MAX || lnum == LONG_MIN) && errno == ERANGE)  
    fprintf(stderr, "ERROR: number out of range for LONG\n");

//Because strtol produces a long, check for overflow
if ( (lnum > INT_MAX) || (lnum < INT_MIN) )
    fprintf(stderr, "ERROR: number out of range for INT\n");

//Finally convert the result to a plain int (if that's what you want)
num = (int) lnum;

注意:如果您确定输入字符串将在有效的 int 范围内,您可以消除 lnum 并直接转换 strtol 的 return:num = (int) strtolen(in_str, &amp;end, 10);

【讨论】:

  • 那里有很多好处,但是...如果sizeof(int) == sizeof(long),您还没有检测到溢出。您应该在调用strtol() 之前设置errno = 0;,然后您需要测试((lnum == LONG_MAX || lnum == LONG_MIN) &amp;&amp; errno == ERANGE) 以发现溢出。 strtol() 虽然可以完成这项工作,但实际上很难正确使用——甚至比你演示的还要难。
  • @JonathanLeffler 好点。我已经相应地更新了我的答案,谢谢。
  • 关于if ( (lnum &gt; INT_MAX) || (lnum &lt; INT_MIN) ),为了与long 溢出对称,if() 块也应该errno = ERANGE; 并将lnum 设置为INT_MAXINT_MIN
【解决方案3】:

你不能。

atoi 无法检测到错误。如果无法表示结果,atoi 会调用未定义的行为。使用strtol 而不是atoi

安全 CERT 编码建议使用 strtol 而不是 atoi,阅读:

INT06-C. Use strtol() or a related function to convert a string token to an integer

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多