实现我自己的 Atoi() 时有一些错误
Have some bugs when implementing my own Atoi()
我听不懂。虽然我的函数 returning,来自 main 中的 char,随机数。原来atoi() returning -1。我目前使用的是 C11 版本。我从某人那里听说,那是因为 int 溢出,我的函数需要 return int,但我目前 returning long。如果不是 2147483647
,我如何检测到 intOverflow
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
bool mx_isdigit(int c) {
return c >= 48 && c <= 57;
}
bool mx_isspace(char c) {
return (c >= 9 && c <= 13) || c == 32;
}
int mx_atoi(const char *str) {
long num = 0;
int sign = 1;
for (; mx_isspace(*str); str++);
if (*str == '-' || *str == '+') {
sign = *str == '-' ? -sign : sign;
str++;
}
for (; *str; str++) {
if (!mx_isdigit(*str)) {
break;
}
num = (num * 10) + (*str - '0');
}
return sign == -1 ? -num : 0 + num;
}
int main(void) {
char str[100] = "12327123061232712306";
printf("R: %d\n", atoi(str));
printf("M: %d", mx_atoi(str));
}
在您的函数 int mx_atoi(const char *str) {...
中,您正在计算 long
类型的结果,而函数 returns 是 int
;因此,如果存储在 long
类型的 num
中的结果不适合 int
,就会丢失一些东西(实际上,由于转换了带符号的整数值,行为是 "implementation-defined",即依赖于编译器)。结果可能会按位截断,产生一个 "looks" 与您输入的十进制数字完全不同的数字。参见,例如,this在线C11草案。粗体段落适用:
6.3.1.3 Signed and unsigned integers
1 When a value with integer type is converted to another integer type
other than _Bool, if the value can be represented by the new type, it
is unchanged.
2 Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or subtracting one more than the maximum value that
can be represented in the new type until the value is in the range of
the new type.60)
3 Otherwise, the new type is signed and the value cannot be
represented in it; either the result is implementation-defined or an
implementation-defined signal is raised.
将int mx_atoi(const char *str)
变为long mx_atoi(const char *str)
,使用long
变量存储结果,不要忘记使用格式说明符%ld
代替%d
在你的 printf
然后。
否则,如果你需要坚持 int
并且你想安全地对溢出做出反应,你可以做类似
if (num > INT_MAX) {
return -1;
}
在你的循环中。 INT_MAX
定义在 limits.h
c >= 48 && c <= 57
不要在代码中使用幻数。使用 '0'
而不是 48
,这样可读性更强,并提供了您的意图。
How can i detect intOverflow
当结果大于类型可以表示的最大值时,就会发生溢出。所以有了数字 a
和 b
我们可以写:
a + b > MAX
但是无法检查这种情况,因为a + b
...会溢出。但是如果我们翻转表达式:
b > MAX - a
可以通过简单的 if
轻松检查。 MAX
是类型的最大值,对于 int
即 INT_MAX
来自 limits.h
.
int mx_atoi(const char *str) {
for (; mx_isspace(*str); str++);
bool negative = false;
if (*str == '-' || *str == '+') {
negative = *str == '-';
str++;
}
int num = 0;
for (; mx_isdigit(*str); str++) {
if (INT_MAX / 10 < num) {
goto ERR_OVERFLOW;
}
num *= 10;
const unsigned char c = *str - '0';
if (INT_MAX - c < num) {
goto ERR_OVERFLOW;
}
num += c;
}
return negative ? -num : num;
ERR_OVERFLOW:
return negative ? INT_MIN : INT_MAX;
}
int
溢出潜力
num = (num * 10) + (*str - '0');
遇到int
溢出,即未定义行为(UB)当:
1) 输入字符串应表示 INT_MIN
和 int/long
具有相同的范围或
2) 输入字符串编码了 int
范围之外的值。
避免这种情况的各种方法。
没有检测到没有数字的字符串
在这种情况下返回 0 是合理的,但代码可能需要设置一些错误条件。
不抱怨尾随非数字
简单地忽略尾随字符是合理的,但代码可能需要设置一些错误条件。
避免int
溢出(并且不依赖long
比int
宽)的方法是在[=20之前进行测试=] 并且由于负数 int
比正数多,因此在负数方面累积。
bool digit_found = false;
int val = 0;
for (; mx_isdigit(*str); str++) {
digit_found = true;
int digit = *str - '[=10=]';
if (val <= INT_MIN/10 && (val < INT_MIN/10 || digit > -(INT_MIN%10))) { // C99
return sign == 1 ? INT_MAX : INT_MIN;
}
val = val * 10 - digit; // note subtraction here
}
if (!digit_found) {
return 0; // Or handle in some other fashion
}
if (sign == 1) {
// If val is too negative to negate ...
if (val < -INT_MAX) {
return INT_MAX; // overflow
}
return -val;
}
return val;
我猜这是最简单的方法。 atoi() 原始使用 LLONG_MAX
检查而不是 LONG_MAX
或 INT_MAX
。所以,尝试我发现的那些限制。如果 (num * 10) + (*str - '0')
将超过 long long
类型的限制,它会将数字转换为 LLONG_MIN
的负值。所以,我创建了 if 语句,用于检查下一次计算是否会小于上一次。如果为真,则返回 0 或 -1。
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
bool mx_isdigit(int c);
bool mx_isspace(char c);
int mx_atoi(const char* str) {
long long num = 0;
int sign = 1;
for (; mx_isspace(*str); str++);
if (*str == '-' || *str == '+') {
sign = *str == '-' ? -sign : sign;
str++;
}
for (; *str; str++) {
if (!mx_isdigit(*str)) {
break;
}
if ((num * 10) + (*str - '0') < num) {
return sign == -1 ? 0 : -1;
}
num = (num * 10) + (*str - '0');
}
return sign == -1 ? -num : num;
}
int main(void) {
char str[100] = "-9223372036854775809";
printf("R: %d\n", atoi(str));
printf("M: %d\n", mx_atoi(str));
}
我听不懂。虽然我的函数 returning,来自 main 中的 char,随机数。原来atoi() returning -1。我目前使用的是 C11 版本。我从某人那里听说,那是因为 int 溢出,我的函数需要 return int,但我目前 returning long。如果不是 2147483647
,我如何检测到 intOverflow#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
bool mx_isdigit(int c) {
return c >= 48 && c <= 57;
}
bool mx_isspace(char c) {
return (c >= 9 && c <= 13) || c == 32;
}
int mx_atoi(const char *str) {
long num = 0;
int sign = 1;
for (; mx_isspace(*str); str++);
if (*str == '-' || *str == '+') {
sign = *str == '-' ? -sign : sign;
str++;
}
for (; *str; str++) {
if (!mx_isdigit(*str)) {
break;
}
num = (num * 10) + (*str - '0');
}
return sign == -1 ? -num : 0 + num;
}
int main(void) {
char str[100] = "12327123061232712306";
printf("R: %d\n", atoi(str));
printf("M: %d", mx_atoi(str));
}
在您的函数 int mx_atoi(const char *str) {...
中,您正在计算 long
类型的结果,而函数 returns 是 int
;因此,如果存储在 long
类型的 num
中的结果不适合 int
,就会丢失一些东西(实际上,由于转换了带符号的整数值,行为是 "implementation-defined",即依赖于编译器)。结果可能会按位截断,产生一个 "looks" 与您输入的十进制数字完全不同的数字。参见,例如,this在线C11草案。粗体段落适用:
6.3.1.3 Signed and unsigned integers
1 When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.60)
3 Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
将int mx_atoi(const char *str)
变为long mx_atoi(const char *str)
,使用long
变量存储结果,不要忘记使用格式说明符%ld
代替%d
在你的 printf
然后。
否则,如果你需要坚持 int
并且你想安全地对溢出做出反应,你可以做类似
if (num > INT_MAX) {
return -1;
}
在你的循环中。 INT_MAX
定义在 limits.h
c >= 48 && c <= 57
不要在代码中使用幻数。使用 '0'
而不是 48
,这样可读性更强,并提供了您的意图。
How can i detect intOverflow
当结果大于类型可以表示的最大值时,就会发生溢出。所以有了数字 a
和 b
我们可以写:
a + b > MAX
但是无法检查这种情况,因为a + b
...会溢出。但是如果我们翻转表达式:
b > MAX - a
可以通过简单的 if
轻松检查。 MAX
是类型的最大值,对于 int
即 INT_MAX
来自 limits.h
.
int mx_atoi(const char *str) {
for (; mx_isspace(*str); str++);
bool negative = false;
if (*str == '-' || *str == '+') {
negative = *str == '-';
str++;
}
int num = 0;
for (; mx_isdigit(*str); str++) {
if (INT_MAX / 10 < num) {
goto ERR_OVERFLOW;
}
num *= 10;
const unsigned char c = *str - '0';
if (INT_MAX - c < num) {
goto ERR_OVERFLOW;
}
num += c;
}
return negative ? -num : num;
ERR_OVERFLOW:
return negative ? INT_MIN : INT_MAX;
}
int
溢出潜力
num = (num * 10) + (*str - '0');
遇到int
溢出,即未定义行为(UB)当:
1) 输入字符串应表示 INT_MIN
和 int/long
具有相同的范围或
2) 输入字符串编码了 int
范围之外的值。
避免这种情况的各种方法。
没有检测到没有数字的字符串
在这种情况下返回 0 是合理的,但代码可能需要设置一些错误条件。
不抱怨尾随非数字
简单地忽略尾随字符是合理的,但代码可能需要设置一些错误条件。
避免int
溢出(并且不依赖long
比int
宽)的方法是在[=20之前进行测试=] 并且由于负数 int
比正数多,因此在负数方面累积。
bool digit_found = false;
int val = 0;
for (; mx_isdigit(*str); str++) {
digit_found = true;
int digit = *str - '[=10=]';
if (val <= INT_MIN/10 && (val < INT_MIN/10 || digit > -(INT_MIN%10))) { // C99
return sign == 1 ? INT_MAX : INT_MIN;
}
val = val * 10 - digit; // note subtraction here
}
if (!digit_found) {
return 0; // Or handle in some other fashion
}
if (sign == 1) {
// If val is too negative to negate ...
if (val < -INT_MAX) {
return INT_MAX; // overflow
}
return -val;
}
return val;
我猜这是最简单的方法。 atoi() 原始使用 LLONG_MAX
检查而不是 LONG_MAX
或 INT_MAX
。所以,尝试我发现的那些限制。如果 (num * 10) + (*str - '0')
将超过 long long
类型的限制,它会将数字转换为 LLONG_MIN
的负值。所以,我创建了 if 语句,用于检查下一次计算是否会小于上一次。如果为真,则返回 0 或 -1。
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
bool mx_isdigit(int c);
bool mx_isspace(char c);
int mx_atoi(const char* str) {
long long num = 0;
int sign = 1;
for (; mx_isspace(*str); str++);
if (*str == '-' || *str == '+') {
sign = *str == '-' ? -sign : sign;
str++;
}
for (; *str; str++) {
if (!mx_isdigit(*str)) {
break;
}
if ((num * 10) + (*str - '0') < num) { return sign == -1 ? 0 : -1; }
num = (num * 10) + (*str - '0');
}
return sign == -1 ? -num : num;
}
int main(void) {
char str[100] = "-9223372036854775809";
printf("R: %d\n", atoi(str));
printf("M: %d\n", mx_atoi(str));
}