重新编码itoa:最小int的错误

时间:2015-12-08 22:39:45

标签: c

我正在尝试重新编码itoa函数,给定一个int,它将返回一个表示其十进制值的字符串。到目前为止,这些功能运作良好:

char        *ft_itoa(int n)
{
    char    s[1024];
    int     i;
    int     neg;

    i = 0;
    neg = 0;
    if (n == 0)
        s[i++] = '0';
    if (n < 0)
    {
        n = n * (-1);
        neg = 1;
    }
    while (n != 0)
    {
        s[i++] = (n % 10) + 48;
        n /= 10;
    }
    if (neg)
        s[i++] = '-';
    s[i] = '\0';
    return (ft_strrev(s));
}

除最小int值外,-2147483648。在这种情况下,函数返回:

"-./,),(-*,("

这是......很奇怪。请注意,ft_strrev将反转结果并对其进行malloc。有线索吗?

修改

这里有很有趣的答案。我特别感兴趣的是缓冲区的最小尺寸。使用limits.h似乎可以解决问题,但我不允许包含除stdlib.h和string.h之外的其他标头。我也受限于三个函数,malloc,free和write。但是,我确实从libc重新编译了strdup和很多函数。

有人可以解释为什么该行会声明我需要的确切内存量:

char   buf[sizeof(int) * CHAR_BIT / 3 + 3];

此外,

  

使用无符号计算数字可以避免INT_MIN的问题。 INT_MIN的错误修复。

为什么?

2 个答案:

答案 0 :(得分:2)

您的代码存在几个小问题:

  • 缓冲区太大了:包括sign和null终止符,24个字节就足够了。对于绝对可移植性,sizeof(int)*CHAR_BIT/3 + 3的上限是正确的。 不是错误,而是浪费

  • 如果您将数字从右向左存储到缓冲区中,则不需要最后的反向阶段,可以直接调用strdup()更简单,更快

  • 使用unsigned计算数字可以避免INT_MIN的问题。 错误修复INT_MIN

  • 循环使用i >= 10并分别存储最后一位数字可以避免0的特殊情况。 更简单,更快,更少的划分

  • 您应该使用'0'而不是硬编码ASCII值48更具可读性和便携性

以下是修改后的版本:

#include <limits.h>

char *ft_itoa(int n) {
    char buf[sizeof(int)*CHAR_BIT/3 + 3];
    char *s;
    unsigned int v;

    v = n;
    if (n < 0) {
        v = -v;
    }
    s = buf + sizeof(buf);
    *--s = '\0';
    while (v >= 10) {
        *--s = '0' + v % 10;
        v /= 10;
    }
    *--s = '0' + v;
    if (n < 0)
        *--s = '-';
    return strdup(s);
}

如果您的系统上没有strdup,那么它很容易实现,并且如果从堆中分配字符串则非常有用。

答案 1 :(得分:1)

避免int溢出(导致OP问题的if (n < 0) { n = n * (-1);)并接受暗(负)方面。由于负数int比正数更负,所以总和为负数。

#include <limits.h>
char *ft_itoa(int n) {
  char s[50];
  // char s[1024];
  int i;
  int neg;

  i = 0;
  neg = 0;
  //if (n == 0)          // special case not need with do loop
  //    s[i++] = '0';

  if (n < 0) {           // Insure n is _not_ positive
    // n = n * (-1);
    neg = 1;
  } else {
    n = -n;              // no overflow possible here
  }
  // while (n != 0)
  do {
    // s[i++] = (n % 10) + 48;
    s[i++] = '0' - (n % 10);   // subtract
    n /= 10;
  } while (n);
  if (neg) s[i++] = '-';
  s[i] = '\0';
  return strdup(strrev(s));
}

对于已清理的版本

#include <limits.h>

// Compute max size need to represent an `int`
#define INT_DEC_SIZE (sizeof (int)*CHAR_BIT/3 + 3)

char *ft_itoa(int n) {
  char s[INT_DEC_SIZE];
  char *p = &s[sizeof s - 1];
  *p = '\0';
  int i = n;
  if (i > 0) {
    i = -i;
  }
  do {
    p--;
    *p = '0' - (i % 10);
    i /= 10;
  } while (i);
  if (n < 0) *(--p) = '-';
  return strdup(p);
}

INT_DEC_SIZE ref