Выделение памяти для массива char для объединения известного фрагмента текста и целого числа - PullRequest
2 голосов
/ 25 февраля 2010

Я хочу объединить фрагмент текста, например «Ответ» с целым числом со знаком, для вывода «Число 42».

Я знаю, какова длина фрагмента текста (14 символов), но я не знаю, сколько символов будет иметь строковое представление числа.

Я предполагаю, что в худшем случае самое большое 16-разрядное целое число со знаком имеет 5 цифр, плюс один дополнительный, если оно отрицательное, так что следующий код - правильный способ сделать это?

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *message;

    message = malloc(14*sizeof(char)+(sizeof(int)*5)+1);

    sprintf(message, "The answer is %d", 42);

    puts(message);

    free(message);
}

Ответы [ 6 ]

7 голосов
/ 25 февраля 2010

Использование:

malloc(14*sizeof(char) /*for the 14 char text*/
       +(sizeof(char)*5) /*for the magnitude of the max number*/
       +1 /* for the sign of the number*/
       +1 /* for NULL char*/
      );

Так как цифры будут представлены в качестве символа вы должны использовать sizeof (char) вместо sizeof (int).

3 голосов
/ 25 февраля 2010

Не совсем, вам нужно только несколько символов, поэтому sizeof(int) не требуется.

Однако, для легко поддерживаемого и переносимого кода у вас должно быть что-то вроде:

#define TEXT "The answer is "
#undef CHARS_PER_INT
#if INT_MAX == 32767
    #define CHARS_PER_INT 6
#endif
#if INT_MAX == 2147483647
    #define CHARS_PER_INT 11
#endif
#ifndef CHARS_PER_INT
    #error Suspect system, I have no idea how many chars to allocate for an int.
#endif

int main (void) {
    char *message;

    message = malloc(sizeof(TEXT)+CHARS_PER_INT+1);
    sprintf(message, TEXT "%d", 42);
    puts(message);
    free(message);
    return 0;
}

Это имеет ряд преимуществ:

  • Если вы измените строку, вы измените только одну вещь. Аргумент malloc корректируется автоматически.
  • Выражение sizeof(TEXT)+CHARS_PER_INT+1 вычисляется во время компиляции. Решение, включающее strlen, будет стоить времени выполнения.
  • Если вы попытаетесь скомпилировать свой код в системе, где целые числа могут вызвать переполнение, вам сообщат об этом (исправьте код).
  • Вы должны фактически выделить дополнительный символ для числа, поскольку наибольшее 16-разрядное число (с точки зрения количества символов) составляет -32768 (шесть символов в длину). Вы заметите, что у меня все еще есть +1 на конце - это потому, что вам нужно место для нулевого ограничителя строки.
1 голос
/ 25 февраля 2010

Один из способов сделать это (не обязательно рекомендуется), который дает точный размер числа в символах, - это использование самих функций stdio.

Например, если вы распечатываете номер (где-то, по любой причине) до того, как выделите свою память, вы можете использовать идентификатор формата %n с printf. %n ничего не печатает; скорее, вы предоставляете ему указатель на int, а printf заполняет его тем, сколько символов было написано до сих пор.

Другой пример - snprintf, если он у вас есть. Вы передаете ему максимальное количество символов, которое вы хотите записать в вашу строку, и оно возвращает количество символов, которое должно было написано , не считая окончательного nul. (Или -1 при ошибке.) Итак, используя 1-байтовую фиктивную строку, snprintf может точно сказать, сколько символов соответствует вашему числу.

Большим преимуществом использования этих функций является то, что если вы решите изменить формат вашего числа (начальные 0, пробельные символы, восьмеричный вывод, длинные длинные значения и т. Д.), Вы не переполните свою память.

Если у вас есть расширения GNU для stdio, вы можете рассмотреть возможность использования asprintf. Это точно так же, как sprintf, за исключением того, что оно выделяет вам память! Сборка не требуется. (Хотя вам нужно освободить его самостоятельно.) Но вы не должны полагаться на то, что он переносим.

0 голосов
/ 26 февраля 2010

Безопасное приближение для подписи int: (количество цифр, включая потенциальный знак -):

(CHAR_BIT * sizeof(int) + 1) / 3 + 1

Эквивалент для unsigned:

(CHAR_BIT * sizeof(unsigned) + 2) / 3

Это вычисляет количество цифр - добавьте один к ним обоим, чтобы учесть терминатор, если выделяете место для строки с нулевым символом в конце.

Это будет немного переоценивать пространство, требуемое для очень длинных типов (и также будет переоценивать в необычном случае, когда int имеет биты заполнения), но является хорошим приближением и имеет то преимущество, что оно константа времени компиляции. CHAR_BIT предоставляется <limits.h>.

0 голосов
/ 25 февраля 2010

Я думаю, что правильная формула для получения максимальной длины десятичного представления целого числа будет (floor (log10 (INT_MAX)) + 1); Вы также можете злоупотреблять препроцессором следующим образом:

#include <limits.h>
#define TOSTRING_(x) #x
#define TOSTRING(x) TOSTRING_(x)
/* ... */
#define YOUR_MESSAGE "The answer is "
char message[]=YOUR_MESSAGE "+" TOSTRING(INT_MAX);
sprintf(message+sizeof(YOUR_MESSAGE),"%d", 42);

, что также позволяет избежать выделения кучи. Возможно, вы захотите использовать snprintf для большей безопасности, хотя с этим методом в этом нет необходимости.

Еще один трюк, подобный этому, - создать такую ​​функцию:

size_t GetIntMaxLenght()
{
    const char dummy[]=TOSTRING(INT_MAX);
    return sizeof(dummy)+1;
}

если компилятор достаточно умен, он может полностью смести переменную dummy из скомпилированного кода, в противном случае может быть целесообразно объявить эту переменную как статическую, чтобы избежать повторной инициализации при каждом вызове функции.

0 голосов
/ 25 февраля 2010

malloc((14 + 6 + 1) * sizeof(char));

  • 14 символов для строки
  • 6 для цифр + знак
  • 1 для '\ 0'

Примечание: Sizeof (int) дает вам размер шрифта в байтах. Sizeof (int) == 4, если int 32 бит, 8, если 64 бит.

...