itoa()c实现int min下溢

时间:2016-10-08 07:42:37

标签: c int overflow itoa

我正在针对我的itoa()函数运行一些测试用例,但不断获取

did not allocate memory for the int min value

我正在检查,但这是我在这里失踪的东西,它是什么?

char *ft_itoa(int x) {
    char *s;
    size_t len;
    long int n;

    n = x;
    if (x == -2147483648)
        return (ft_strdup("-2147483648"));

    len = ft_intlen(n) + 1;
    if (!(s = (char*)malloc(sizeof(char) * len)))
        return (NULL);

    if (n == 0)
        s[0] = '0';

    if (n < 0) {
        s[0] = '-';
        n = -n;
    }
    s[len - 1] = '\0';
    while (n) {
        len--;
        s[len - 1] = (n % 10) + '0';
        n /= 10;
    }
    return (s);
}

7 个答案:

答案 0 :(得分:10)

这一行:

if (x == -2147483648)

不符合您的想法。 C没有负整数常量。这是一个无符号的int常量,值为2 ^ 31,您应用一元减号运算符。这意味着表达式x == -21...将取决于编译器使用的C标准。

如果你使用C99或C11,你会没事的。有一个足够大的签名类型 - 长期保证足够大,这个数字,所以x和-21 ...将被转换为long long然后进行比较。但是如果你使用的是C89编译器并且你的机器没有足够长的类型,那么你就会遇到实现定义的行为:

  

当一个整数降级为具有较小大小的有符号整数,或者无符号整数转换为其对应的有符号整数时,如果该值无法表示,则结果是实现定义的。

这就是人们说使用limits.h的原因。不是因为他们是迂腐,而是因为这是危险的领域。如果你仔细观察limits.h包含的内容,你很可能会找到这样的一行:

#define INT_MIN (- INT_MAX - 1)

此表达式实际上具有正确的类型和值。

除此之外,我在您发布的代码中看不到任何错误。如果这不是问题ft_intlenft_strdup是错误的。或者你正在测试你的函数测试错误(同样的问题适用于-21 ......调用测试时)。

答案 1 :(得分:2)

状态:已解决无效

原因:WORKS_FOR_ME

无论如何,我在某些方面做了改进。

  • sizeof(char)始终为1,不需要它。
  • 不要施放malloc
  • 如果您处理特殊情况0,则只需一次处理即可。
  • -2147483648非常糟糕。这是INT_MIN的用途。
  • 返回不是功能,请勿返回(value),只需返回value
  • 始终s[len - 1],在进入循环之前更好地减少len。或者,由于您仅在len + 1来电中需要malloc,只需len intlen将其返回并使用malloc致电len + 1

<强> ft_itoa.c

#include <stdbool.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <btstr.h>

int ft_intlen(int n) {
        char buffer[8192];
        return snprintf(buffer, sizeof buffer, "%i", n);
}

char * ft_itoa(int n) {
        char * s;
        size_t l, len;
        bool fix_int_min = false;

        if (!n) {
                return mstrcpy("0");
        }

        if (-INT_MAX != INT_MIN && n == INT_MIN) {
                ++n;
                fix_int_min = true;
        }

        len = ft_intlen(n);
        if (!(s = malloc(len + 1))) {
                return NULL;
        }
        if (n < 0) {
                s[0] = '-';
                n = -n;
        }
        s[l = len] = '\0';
        while (n) {
                s[--len] = (n % 10) + '0';
                n /= 10;
        }

        if (fix_int_min) {
                --l;
                while (s[l] == '9') {
                        s[l++] = 0;
                }
                if (s[l] == '-') {
                        // realloc +1 and write "-1[0....0]\0"
                } else {
                        ++s[l];
                }
        }

        return s;
}

<强>的main.c

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

char * ft_itoa(int n);

void check(int n) {
        printf("%i = %s\n", n, ft_itoa(n));
}

int main() {
        check(0);
        check(-1);
        check(1);
        check(23);
        check(42);
        check(4711);
        check(1000);
        check(INT_MAX);
        check(1+INT_MIN);
        check(INT_MIN);
}

<强>结果

$ gcc -W -Wall -Wextra -lBtLinuxLibrary ft_itoa.c main.c -o ft_itoa && ./ft_itoa
0 = 0
-1 = -1
1 = 1
23 = 23
42 = 42
4711 = 4711
1000 = 1000
2147483647 = 2147483647
-2147483647 = -2147483647
-2147483648 = -2147483648

答案 2 :(得分:1)

您不需要检查。而是将其转换为unsigned,这将符合绝对值:

size_t ft_uintlen(unsigned n)
{
    size_t len = 0;
    do {
        ++len;
        n /= 10;
    } while(n);
    return len;
}

char *ft_itoa(int x)
{
    char    *s;
    size_t  len;
    unsigned n;
    int negative;

    negative = x < 0;
    n = negative ? 0-(unsigned)x : (unsigned)x;
    len = ft_uintlen(n) + negative + 1;
    if (!(s = (char*)malloc(len)))
        return (NULL);

    s[--len] = '\0';
    if (negative)
        s[0] = '-';
    do {
        s[--len] = (n % 10) + '0';
        n /= 10;
    } while(n);
    return (s);
}

请注意,这使用了一个适用于size_t ft_uintlen(unsigned)参数的新unsigned函数。

答案 3 :(得分:0)

溢出预防机制可能存在问题。您尝试将x类型int分配给类型为n的{​​{1}}。但规范并不保证类型long int可以处理大于long int的值范围。可以找到更多信息"Long Vs. Int"

如果您的编译器支持int,请使用long long int类型。将您的n功能更新为ft_intlen。在这种情况下,您将能够处理整个int ft_intlen(long long int n)类型值范围并删除以下行:

int

此外,错误消息if (x == -2147483648) return (ft_strdup("-2147483648")); 不是system error numbers之一。您需要在应用程序中添加更多日志记录,特别是如果由于某种原因无法对其进行调试。检查每个系统函数调用的did not allocate memory for the int min value,例如:

errno

答案 4 :(得分:0)

潜在的代码失败,以怀疑的顺序:

  1. ft_strdup()因为该代码使用“int min value”调用并发生错误。
  2. 缺乏各种功能的原型。特别是ft_strdup()/strdup()
  3. 调用/测试代码有问题。
  4. “int min value”大于-2147483648。 (最好使用INT_MIN。)
  5. ft_intlen(n)编码错误并返回INT_MAX,然后代码尝试malloc(INT_MIN)
  6. int/long都是64位。这会使第一个s[len - 1] = (n % 10) + '0';INT_MIN混淆。
  7. 否则,如果INT_MIN的值为-2147483648,则ft_itoa(int x)就可以了。

    OP断言“... strdup只是分配字符串,ft_intlen只返回字符串的长度,两者都通过测试用例 - franklinexpress 10月8日7:52”

    传递测试用例并不意味着它可以在不调用未定义的行为的情况下工作。最好发布ft_intlen()ft_strdup()和测试工具以供审核。

    候选便携式实施。不依赖于int/long大小或2的补码。除了<limits.h>之外,CHAR_BIT除了{em> 适用于C89 / 99/11。

    // Buffer size needed to decimal print any `int`
    // '-' + Ceiling(value bit size * log10(2)) + \0
    #define INT_STR_SIZE (1 + ((CHAR_BIT*sizeof(int) - 1)/3 + 1) + 1)
    
    char *ft_itoa(int x) {
      char buf[INT_STR_SIZE];
      char *s = buf + sizeof buf - 1;  // Set to end of buffer
      *s = '\0';
    
      int n = x; // no need for wider types like long
    
      if (n > 0) {
        // fold positive numbers to negative ones
        // This avoids the special code for `INT_MIN` and need for wider types
        n = -n;
      }
    
      // Using a do loop avoids special code for `x==0`
      do {
        // Use `div()` rather than / % in case we are using C89.
        // / %  has implementation defined results for negative arguments.
        div_t qr = div(n, 10);
        *--s = (char) ('0' - qr.rem);  // Form digit from negative .rem
        n = qr.quot;
      } while (n);
    
      if (x < 0) {
        *--s = '-';
      }
    
      // Double check ft_strdup() is coded correctly
      // Insure calling code frees the buffer when done.
      return ft_strdup(s); 
    }
    

答案 5 :(得分:0)

您提供的代码片段可以编译并在OsX上工作,但使用我自己的ft_stdupft_intlen。因此,您可以向我们展示代码或检查错误。 我做了一些测试(包括2147483647,-2147483648)。它工作得很好。

无论如何,行:

if (x == -2147483648) return (ft_strdup("-2147483648"));

只要您在执行任何操作之前将x值复制到long long变量(Art)中,就无用了。所以你不需要包括types.h(臭名昭着的moulinette不会给你-42)。

在OsX上它也适用于long值,但这是非便携式安全的。

答案 6 :(得分:0)

只需使用:

INT_MIN

而不是:

-2147483648

在你的测试中:

if (x == INT_MIN)
    return (ft_strdup("-2147483648"));

原因是某些编译器在理解该数字时可能会遇到问题。

标准C库 limits.h 通常将其定义为:

#define INT_MIN  (-INT_MAX - 1)

避免这个问题。