实现良好的“itoa()”function的正确方法是什么?

我想知道我执行“itoa”function是否正确。 也许你可以帮我把它变得更“正确”,我很确定我错过了什么。 (也许已经有一个库按我想要的方式进行转换,但是……找不到任何东西)

#include  #include  #include  #include  char * itoa(int i) { char * res = malloc(8*sizeof(int)); sprintf(res, "%d", i); return res; } int main(int argc, char *argv[]) { ... 

唯一的实际错误是您没有检查malloc的返回值是否为null。

itoa的名称已经被用于非标准的function,但并非罕见。 它不分配内存,而是写入调用者提供的缓冲区:

 char *itoa(int value, char * str, int base); 

如果你不想依赖你的平台,我仍然会建议遵循这种模式。 在C中返回新分配的内存的字符串处理函数通常比它们在长期运行中的价值更麻烦,因为大多数时候你最终会进行进一步的操作,因此你必须释放大量的中间结果。 例如,比较:

 void delete_temp_files() { char filename[20]; strcpy(filename, "tmp_"); char *endptr = filename + strlen(filename); for (int i = 0; i < 10; ++i) { itoa(endptr, i, 10); // itoa doesn't allocate memory unlink(filename); } } 

 void delete_temp_files() { char filename[20]; strcpy(filename, "tmp_"); char *endptr = filename + strlen(filename); for (int i = 0; i < 10; ++i) { char *number = itoa(i, 10); // itoa allocates memory strcpy(endptr, number); free(number); unlink(filename); } } 

如果您有理由特别关注性能(例如,如果您正在实现包含itoa的stdlib样式库),或者如果您正在实现sprintf不支持的基础,那么您可能会考虑不调用sprintf 。 但是如果你想要一个基数为10的字符串,那么你的第一直觉是正确的。 关于%d格式说明符绝对没有“不正确”。

以下是itoa的可能实现,仅适用于基数10:

 char *itobase10(char *buf, int value) { sprintf(buf, "%d", value); return buf; } 

这是一个将snprintf风格的方法结合到缓冲区长度的方法:

 int itobase10n(char *buf, size_t sz, int value) { return snprintf(buf, sz, "%d", value); } 
 // Yet, another good itoa implementation // returns: the length of the number string int itoa(int value, char *sp, int radix) { char tmp[16];// be careful with the length of the buffer char *tp = tmp; int i; unsigned v; int sign = (radix == 10 && value < 0); if (sign) v = -value; else v = (unsigned)value; while (v || tp == tmp) { i = v % radix; v /= radix; // v/=radix uses less CPU clocks than v=v/radix does if (i < 10) *tp++ = i+'0'; else *tp++ = i + 'a' - 10; } int len = tp - tmp; if (sign) { *sp++ = '-'; len++; } while (tp > tmp) *sp++ = *--tp; return len; } // Usage Example: char int_str[15]; // be careful with the length of the buffer int n = 56789; int len = itoa(n,int_str,10); 

我认为你分配的内存太多了。 malloc(8*sizeof(int))在大多数机器上都会给你32个字节,这对于int的文本表示来说可能是过多的。

我不太清楚8*sizeof(int)的最大可能字符数 – ceil(8 / (log(10) / log(2)))乘以3* 。 此外,在C99和一些较旧的POSIX平台下,您可以使用sprintf()创建精确分配的版本:

 char * itoa(int i) { int n = snprintf(NULL, 0, "%d", i) + 1; char *s = malloc(n); if (s != NULL) snprintf(s, n, "%d", i); return s; } 

HTH

我找到了一个有趣的资源来处理itoa实现的几个不同问题
你可能也想看一下
具有性能测试的itoa()实现

为此,您应该在printf系列中使用一个函数。 如果您要将结果写入stdout或文件,请使用printf / fprintf 。 否则,请使用snprintf ,其缓冲区大小足以容纳3*sizeof(type)+2字节或更多字节。

itoa()

适用于[INT_MIN...INT_MAX] ,基础[2...36]
不需要2的补码。
不要求unsigned具有比int更大的正范围 – 它不使用unsigned
不假设int大小。

注意:对于负数使用'-' ,即使base != 10

根据需要定制error handling。

 char* itostr(char *dest, size_t size, int a, int base) { static char buffer[sizeof a * CHAR_BIT + 1 + 1]; static const char digits[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (base < 2 || base > 36) { fprintf(stderr, "Invalid base"); return NULL; } char* p = &buffer[sizeof(buffer) - 1]; *p = '\0'; int an = a < 0 ? a : -a; // Works with negative `int` do { *(--p) = digits[-(an % base)]; an /= base; } while (an); if (a < 0) { *(--p) = '-'; } size_t size_used = &buffer[sizeof(buffer)] - p; if (size_used > size) { fprintf(stderr, "Scant buffer %zu > %zu", size_used , size); return NULL; } return memcpy(dest, p, size_used); } 

这应该工作:

 #include  #include  #include  char * itoa_alloc(int x) { int s = x<=0 ? 1 ? 0; // either space for a - or for a 0 size_t len = (size_t) ceil( log10( abs(x) ) ); char * str = malloc(len+s + 1); sprintf(str, "%i", x); return str; } 

如果您不想使用数学/浮点函数(并且必须在数学库中链接),您应该能够通过搜索Web找到log10的非浮点版本并执行:

size_t len = my_log10(abs(x))+ 1;

这可能比你需要多1个字节,但你已经足够了。

我可能会提出一些建议。 您可以使用静态缓冲区和strdup来避免在后续调用中重复分配太多内存。 我还会添加一些错误检查。

 char *itoa(int i) { static char buffer[12]; if (snprintf(buffer, sizeof(buffer), "%d", i) < 0) return NULL; return strdup(buffer); } 

如果在multithreading环境中调用它,请从缓冲区声明中删除“static”。

sprintf非常慢,如果性能很重要,它可能不是最好的解决方案。

如果基本参数是2的幂,则可以通过移位和屏蔽来完成转换,并且可以通过记录来自最高位置的数字来避免反转字符串。 例如,base = 16就是这样的

 int num_iter = sizeof(int) / 4; 

const char digits [] = {‘0’,’1’,’2’,’3’,’4’,’5’,’6’,’7’,’8’,’9’,’a’ ,’b’,’c’,’d’,’e’,’f’};

 /* skip zeros in the highest positions */ int i = num_iter; for (; i >= 0; i--) { int digit = (value >> (bits_per_digit*i)) & 15; if ( digit > 0 ) break; } for (; i >= 0; i--) { int digit = (value >> (bits_per_digit*i)) & 15; result[len++] = digits[digit]; } 

对于小数,有一个很好的想法,使用足够大的静态数组以相反的顺序记录数字,请参见此处

 main() { int i=1234; char stmp[10]; #if _MSC_VER puts(_itoa(i,stmp,10)); #else puts((sprintf(stmp,"%d",i),stmp)); #endif return 0; }