我正在尝试重新编码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的错误修复。
为什么?
答案 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);
}