Мне было интересно, правильна ли моя реализация функции "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[]) {
...
Единственная фактическая ошибка заключается в том, что вы не проверяете возвращаемое значение malloc
на null.
Название itoa
вроде как уже занято для нестандартной, но не такой уж редкой функции. Она не выделяет память, а записывает в буфер, предоставляемый вызывающей стороной:
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);
}
}
Если бы у вас были причины особенно беспокоиться о производительности (например, если вы реализуете библиотеку в стиле stdlib, включая itoa
), или если вы реализуете основания, которые sprintf
не поддерживает, то вы могли бы рассмотреть возможность отказа от вызова sprintf
. Но если вам нужна строка с основанием 10, то ваш первый инстинкт был верен. Нет абсолютно ничего "неправильного" в спецификаторе формата %d
.
Вот возможная реализация itoa
, только для base 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);
}
Я думаю, что вы выделяете слишком много памяти. 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
байт или больше.
Это должно работать:
#include <string.h>
#include <stdlib.h>
#include <math.h>
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;
}
Если вы не хотите использовать математические функции / функции с плавающей запятой (и должны связываться в математических библиотеках), вы должны иметь возможность найти версии 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);
}
Если это будет вызываться в многопоточной среде, уберите "static" из объявления буфера.
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;
}