"string of digits into its numeric equivalent" 示例无法根据 K&R 正常工作

"string of digits into its numeric equivalent" example not working properly as per K&R

我试图从 stdlib.h 中找出函数 atoi()。根据 K&R,它看起来像以下内容:

int atoi(char s[]) {
   int n, i;

   n = 0;
   for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
        n = 10 * n + (s[i] - '0');
   return n;
}

据我了解,来自 stdlib.hatoi() 函数应该获取任何字符的字符串作为输入,并且只输出数字,如下所示:

代码 1:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("%i", atoi(" -123junk"));
    printf("%i", atoi("0"));
    printf("%i", atoi("junk"));         // no conversion can be performed
    printf("%i", atoi("2147483648"));   // UB: out of range of int
}

输出:

-123
0
0
-2147483648

但是,在我的程序中,我尝试提供字符串作为输入并仅获取数字作为输出:

代码 2:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int c, i;
    char s[i];
    for (i = 0; (c = getchar()) != '\n'; ++i)
       s[i] = c;
    s[i] = '\n';
    s[++i] = '[=14=]';
    printf("%i", atoi(s));
}

在机器上执行时:

pi@host:~/new$ cc new.c
pi@host:~/new$ a.out
21412421How it is
0

我得到的输出值不正确。

问题:

1) 根据代码 1,printf("%i", atoi(" -123junk")),看起来函数 atoi() 可以接收字符串作为参数和 return 表示数值串联的单个整数输入字符串中的数字,不是吗?

2) atoi()stdlib.h、return 是什么意思?

3) 如何修复代码 2 中的函数 main(),以便从 stdin 中获取字符,写入数组,调用函数 atoi(),提供数组作为一个参数,从字面上看,输出中的数字?

4) 根据函数atoi(), "the expression (s[i] - '0') is the numeric value of the character stored in s[i]"的K&R示例,但是,为什么我们需要添加10 * n部分,而且要分配n到它之前的 0,如 n * 0 = 0,因此,n * 10 = 0,这意味着 n * 10 在赋值语句 n = 10 * n + (s[i] - '0'); 中将始终为零,因此,为什么我们需要它?

5) 如果 atoi() 是 returning 一个整数,那么 printf("%i", atoi(" -123junk")); return 的结果怎么可能是一个带有数字 -123 的字符串?换句话说,我的理解是否正确:函数 atoi() 是在函数 printf() 中以 " -123junk" 作为参数调用的。函数atoi()returns整数,只有一个整数,有点像n = 10 * n + (s[i] - '0');比,怎么在-123展开??

Q1) 您的 atoi() 版本太简单了,标准版本会忽略前导空白字符并处理数字前的可选符号。 atoi(" -123junk") 应评估为 -123.

Q2) atoi 是一个用原型 int atoi(const char *s); 定义的标准函数,它 returns 一个整数。

Q3) Code 2有几个错误:

  • 您将 char 数组 s 的大小指定为 i,这是未初始化的。您应该改为使用相当大的值定义数组,例如 64
  • 您应该在循环中测试潜在的缓冲区溢出,
  • 您应该检查 EOF 以停止循环,以防在没有换行符的情况下遇到文件结尾。

这是修改后的版本:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char s[64];
    size_t i;
    int c;
    for (i = 0; i < sizeof(s) - 1 && (c = getchar()) != EOF;) {
       s[i++] = c;
       if (c == '\n')
           break;
    }
    s[i] = '[=10=]';
    printf("%i", atoi(s));
    return 0;
}

Q4) 表达式 n = 10 * n + (s[i] - '0') 为在字符串中找到的每个新数字求值。只要没有遇到非零数字,就将当前值乘以10确实效率稍低,但是这样写函数很简单。

为了避免这些无用的乘法,这里有一个替代方法:

int atoi(const char *s) {
    int n = 0;
    size_t i = 0;

    while (s[i] == '0')
        i++;
    if (s[i] >= '1' && s[i] <= '9') {
        n = s[i++] - '0';
        while (s[i] >= '0' && s[i] <= '9')
            n = 10 * n + (s[i++] - '0');
    }
    return n;
}

但是这个功能比较繁琐,实际上可能不如简单版本有效。尝试在您的系统上进行基准测试。

为了完整起见,这里是一个完整的可移植版本,使用 ctype.h> 处理可选的初始空格和一个可选的符号。它还通过定义的行为处理溢出,尽管 atoi() 的标准版本不需要这样做。

#include <limits.h>
#include <stdio.h>

int atoi(const char *s) {
    int n = 0, d;

    /* skip optional initial white space */
    while (isspace((unsigned char)*s))
        s++;
    if (*s == '-') {
        /* convert negative number */
        s++;
        while (isdigit((unsigned char)*s)) {
            d = (*s++ - '0');
            /* check for potential arithmetic overflow */
            if (n < INT_MIN / 10 || (n == INT_MIN / 10 && -d < INT_MIN % 10)) {
                n = INT_MIN;
                break;
            }
            n = n * 10 - d;
        }
    } else {
        /* ignore optional positive sign */
        if (*s == '+')
            s++;
        while (isdigit((unsigned char)*s)) {
            d = (*s++ - '0');
            /* check for potential arithmetic overflow */
            if (n > INT_MAX / 10 || (n == INT_MAX / 10 && d > INT_MAX % 10)) {
                n = INT_MAX;
                break;
            }
            n = n * 10 + d;
        }
    }
    return n;
}

int main(int argc, char *argv[]) {
    int i, n;

    for (i = 1; i < argc; i++) {
        n = atoi(argv[i]);
        printf("\"%s\" -> %d\n", argv[i], n);
    }
    return 0;
}