"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.h
的 atoi()
函数应该获取任何字符的字符串作为输入,并且只输出数字,如下所示:
代码 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;
}
我试图从 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.h
的 atoi()
函数应该获取任何字符的字符串作为输入,并且只输出数字,如下所示:
代码 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;
}