C语言 实现一个好的“itoa()”函数的正确方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3440726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the proper way of implementing a good "itoa()" function?
提问by Nicolas C.
I was wondering if my implementation of an "itoa" function is correct. Maybe you can help me getting it a bit more "correct", I'm pretty sure I'm missing something. (Maybe there is already a library doing the conversion the way I want it to do, but... couldn't find any)
我想知道我对“itoa”函数的实现是否正确。也许你可以帮助我让它更“正确”,我很确定我错过了一些东西。(也许已经有一个库按照我想要的方式进行转换,但是……找不到任何库)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char * itoa(int i) {
char * res = malloc(8*sizeof(int));
sprintf(res, "%d", i);
return res;
}
int main(int argc, char *argv[]) {
...
采纳答案by Steve Jessop
The only actual error is that you don't check the return value of mallocfor null.
唯一的实际错误是您没有检查mallocnull的返回值。
The name itoais kind of already taken for a function that's non-standard, but not that uncommon. It doesn't allocate memory, rather it writes to a buffer provided by the caller:
这个名字itoa有点像一个非标准的函数,但并不少见。它不分配内存,而是写入调用者提供的缓冲区:
char *itoa(int value, char * str, int base);
If you don't want to rely on your platform having that, I would still advise following the pattern. String-handling functions which return newly allocated memory in C are generally more trouble than they're worth in the long run, because most of the time you end up doing further manipulation, and so you have to free lots of intermediate results. For example, compare:
如果你不想依赖你的平台,我仍然建议遵循这种模式。从长远来看,在 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);
}
}
vs.
对比
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);
}
}
If you had reason to be especially concerned about performance (for instance if you're implementing a stdlib-style library including itoa), or if you were implementing bases that sprintfdoesn't support, then you might consider not calling sprintf. But if you want a base 10 string, then your first instinct was right. There's absolutely nothing "incorrect" about the %dformat specifier.
如果您有理由特别关注性能(例如,如果您正在实现一个 stdlib 风格的库,包括itoa),或者如果您正在实现sprintf不支持的基础,那么您可能会考虑不调用sprintf. 但是如果你想要一个以 10 为基数的字符串,那么你的第一直觉是对的。%d格式说明符绝对没有“不正确”的地方。
Here's a possible implementation of itoa, for base 10 only:
这是 , 的可能实现itoa,仅适用于基数 10:
char *itobase10(char *buf, int value) {
sprintf(buf, "%d", value);
return buf;
}
Here's one which incorporates the snprintf-style approach to buffer lengths:
这是一种结合了 snprintf 风格的缓冲区长度方法:
int itobase10n(char *buf, size_t sz, int value) {
return snprintf(buf, sz, "%d", value);
}
回答by Minh Nguyen
// 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);
回答by kbrimington
I think you are allocating perhaps too much memory. malloc(8*sizeof(int))will give you 32 bytes on most machines, which is probably excessive for a text representation of an int.
我认为您分配的内存可能过多。malloc(8*sizeof(int))在大多数机器上会给你 32 个字节,这对于 int 的文本表示可能是过多的。
回答by chux - Reinstate Monica
A good intto stringor itoa()has these properties;
一个好的int到字符串或itoa()具有这些属性;
- Works for all
[INT_MIN...INT_MAX], base[2...36]without buffer overflow. - Does not assume
intsize. - Does not require 2's complement.
- Does not require
unsignedto have a greater positive range thanint. In other words, does not useunsigned. - Allows use of
'-'for negative numbers, even whenbase != 10.
- 适用于所有
[INT_MIN...INT_MAX]基础,[2...36]无缓冲区溢出。 - 不假设
int大小。 - 不需要 2 的补码。
- 不需要
unsigned比 更大的正范围int。换句话说,不使用unsigned. - 允许
'-'对负数使用,即使在base != 10.
Tailor the error handling as needed. (needs C99 or later):
根据需要定制错误处理。(需要 C99 或更高版本):
char* itostr(char *dest, size_t size, int a, int base) {
// Max text needs occur with itostr(dest, size, INT_MIN, 2)
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;
}
// Start filling from the end
char* p = &buffer[sizeof buffer - 1];
*p = '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;
}
';
// Work with negative `int`
int an = a < 0 ? a : -a;
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);
}
回答by llasram
I'm not quite sure where you get 8*sizeof(int)as the maximum possible number of characters -- ceil(8 / (log(10) / log(2)))yields a multiplier of 3*. Additionally, under C99 and some older POSIX platforms you can create an accurately-allocating version with sprintf():
我不太确定你在哪里得到8*sizeof(int)最大可能的字符数 -ceil(8 / (log(10) / log(2)))产生3*. 此外,在 C99 和一些较旧的 POSIX 平台下,您可以使用以下命令创建准确分配的版本sprintf():
int num_iter = sizeof(int) / 4;
HTH
HTH
回答by Adam
i found an interesting resource dealing with several different issues with the itoa implementation
you might wanna look it up too
itoa() implementations with performance tests
我发现了一个有趣的资源,用于处理与 itoa 实现有关的几个不同问题,
您可能也想查找
itoa() 实现与性能测试
回答by R.. GitHub STOP HELPING ICE
You should use a function in the printffamily for this purpose. If you'll be writing the result to stdoutor a file, use printf/fprintf. Otherwise, use snprintfwith a buffer big enough to hold 3*sizeof(type)+2bytes or more.
printf为此,您应该使用族中的函数。如果要将结果写入stdout文件或文件,请使用printf/ fprintf。否则,请使用snprintf足以容纳3*sizeof(type)+2字节或更多字节的缓冲区。
回答by Sergey Solovyev
sprintf is quite slow, if performance matters it is probably not the best solution.
sprintf 很慢,如果性能很重要,它可能不是最好的解决方案。
if the base argument is a power of 2 the conversion can be done with a shift and masking, and one can avoid reversing the string by recording the digits from the highest positions. For instance, something like this for base=16
如果基本参数是 2 的幂,则可以通过移位和屏蔽来完成转换,并且可以通过记录最高位置的数字来避免反转字符串。例如,像这样的 base=16
/* 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];
}
const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
const char 数字[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a' , 'b', 'c', 'd', 'e', 'f'};
uint8_t my_itoa(int32_t data, uint8_t *ptr, uint32_t base){
uint8_t cnt=0,sgnd=0;
uint8_t *tmp=calloc(32,sizeof(*tmp));
if(!tmp){exit(1);}
else{
for(int i=0;i<32;i++){
if(data<0){data=-data;sgnd=1;}
if(data!=0){
if(data%base<10){
*(tmp+i)=(data%base)+48;
data/=base;
}
else{
*(tmp+i)=(data%base)+55;
data/=base;
}
cnt++;
}
}
if(sgnd){*(tmp+cnt)=45;++cnt;}
}
my_reverse(tmp, cnt);
my_memcopy(tmp,ptr,cnt);
return ++cnt;
}
For decimals there is a nice idea to use a static array big enough to record the numbers in the reversed order, see here
对于小数,使用足够大的静态数组来以相反的顺序记录数字是一个不错的主意,请参见此处
回答by Joseph Soliman
- Integer-to-ASCII needs to convert data from a standard integer type into an ASCII string.
- All operations need to be performed using pointer arithmetic, not array indexing.
- The number you wish to convert is passed in as a signed 32-bit integer.
- You should be able to support bases 2 to 16 by specifying the integer value of the base you wish to convert to (base).
- Copy the converted character string to the uint8_t* pointer passed in as a parameter (ptr).
- The signed 32-bit number will have a maximum string size (Hint: Think base 2).
- You must place a null terminator at the end of the converted c-string Function should return the length of the converted data (including a negative sign).
- Example my_itoa(ptr, 1234, 10) should return an ASCII string length of 5 (including the null terminator).
- This function needs to handle signed data.
- You may not use any string functions or libraries.
- Integer-to-ASCII 需要将数据从标准整数类型转换为 ASCII 字符串。
- 所有操作都需要使用指针算法来执行,而不是数组索引。
- 您希望转换的数字作为有符号的 32 位整数传入。
- 您应该能够通过指定要转换为 (base) 的基数的整数值来支持基数 2 到 16。
- 将转换后的字符串复制到作为参数(ptr)传入的uint8_t*指针。
- 有符号的 32 位数字将具有最大字符串大小(提示:以 2 为基数)。
- 您必须在转换后的 c-string 末尾放置一个空终止符 函数应返回转换后数据的长度(包括负号)。
- 示例 my_itoa(ptr, 1234, 10) 应返回长度为 5 的 ASCII 字符串(包括空终止符)。
- 该函数需要处理签名数据。
- 您不得使用任何字符串函数或库。
.
.
int32_t my_atoi(uint8_t *ptr, uint8_t digits, uint32_t base){
int32_t sgnd=0, rslt=0;
for(int i=0; i<digits; i++){
if(*(ptr)=='-'){*ptr='0';sgnd=1;}
else if(*(ptr+i)>'9'){rslt+=(*(ptr+i)-'7');}
else{rslt+=(*(ptr+i)-'0');}
if(!*(ptr+i+1)){break;}
rslt*=base;
}
if(sgnd){rslt=-rslt;}
return rslt;
}
- ASCII-to-Integer needs to convert data back from an ASCII represented string into an integer type.
- All operations need to be performed using pointer arithmetic, not array indexing
- The character string to convert is passed in as a uint8_t * pointer (ptr).
- The number of digits in your character set is passed in as a uint8_t integer (digits).
- You should be able to support bases 2 to 16.
- The converted 32-bit signed integer should be returned.
- This function needs to handle signed data.
- You may not use any string functions or libraries.
- ASCII-to-Integer 需要将数据从 ASCII 表示的字符串转换回整数类型。
- 所有操作都需要使用指针算法来执行,而不是数组索引
- 要转换的字符串作为 uint8_t * 指针 (ptr) 传入。
- 字符集中的位数作为 uint8_t 整数(数字)传入。
- 您应该能够支持 2 到 16 个基数。
- 应返回转换后的 32 位有符号整数。
- 该函数需要处理签名数据。
- 您不得使用任何字符串函数或库。
.
.
char *itoa(int i)
{
static char buffer[12];
if (snprintf(buffer, sizeof(buffer), "%d", i) < 0)
return NULL;
return strdup(buffer);
}
回答by Brandon Horsley
There a couple of suggestions I might make. You can use a static buffer and strdup to avoid repeatedly allocating too much memory on subsequent calls. I would also add some error checking.
我可能会提出一些建议。您可以使用静态缓冲区和 strdup 来避免在后续调用中重复分配过多内存。我还会添加一些错误检查。
##代码##If this will be called in a multithreaded environment, remove "static" from the buffer declaration.
如果这将在多线程环境中调用,请从缓冲区声明中删除“静态”。

