实现我自己的 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

当结果大于类型可以表示的最大值时,就会发生溢出。所以有了数字 ab 我们可以写:

a + b > MAX

但是无法检查这种情况,因为a + b...会溢出。但是如果我们翻转表达式:

b > MAX - a

可以通过简单的 if 轻松检查。 MAX 是类型的最大值,对于 intINT_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_MINint/long 具有相同的范围或
2) 输入字符串编码了 int 范围之外的值。

避免这种情况的各种方法。

没有检测到没有数字的字符串

在这种情况下返回 0 是合理的,但代码可能需要设置一些错误条件。

不抱怨尾随非数字

简单地忽略尾随字符是合理的,但代码可能需要设置一些错误条件。


避免int溢出(并且依赖longint宽)的方法是在[=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_MAXINT_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));
}