Как правильно реализовать хорошую функцию «itoa ()»?

Мне было интересно, правильна ли моя реализация функции "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[]) {
 ...
9
задан idmean 18 December 2015 в 11:23
поделиться

8 ответов

Единственная фактическая ошибка заключается в том, что вы не проверяете возвращаемое значение 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);
}
7
ответ дан 4 December 2019 в 07:22
поделиться

Я думаю, что вы выделяете слишком много памяти. malloc (8 * sizeof (int)) даст вам 32 байта на большинстве машин, что, вероятно, чрезмерно для текстового представления int.

3
ответ дан 4 December 2019 в 07:22
поделиться

Я не совсем понимаю, откуда вы взяли 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

2
ответ дан 4 December 2019 в 07:22
поделиться

я нашел интересный ресурс, посвященный нескольким различным проблемам с реализацией itoa

itoa() реализации с тестами производительности

2
ответ дан 4 December 2019 в 07:22
поделиться

Для этой цели следует использовать функцию семейства printf. Если вы будете записывать результат в stdout или файл, используйте printf/fprintf. В противном случае используйте snprintf с буфером, достаточно большим, чтобы вместить 3*sizeof(type)+2 байт или больше.

1
ответ дан 4 December 2019 в 07:22
поделиться

Это должно работать:

#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 байт больше, чем вам нужно, но вы бы достаточно.

0
ответ дан 4 December 2019 в 07:22
поделиться

Есть пара предложений, которые я мог бы сделать. Вы можете использовать статический буфер и strdup, чтобы избежать повторного выделения слишком большого объема памяти при последующих вызовах. Я бы также добавил некоторую проверку ошибок.

char *itoa(int i)
{
  static char buffer[12];

  if (snprintf(buffer, sizeof(buffer), "%d", i) < 0)
    return NULL;

  return strdup(buffer);
}

Если это будет вызываться в многопоточной среде, уберите "static" из объявления буфера.

0
ответ дан 4 December 2019 в 07:22
поделиться
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;
}
-1
ответ дан 4 December 2019 в 07:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: