Выделение памяти для массива char для конкатенации известной части текста и целого числа

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

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

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

#include  #include  int main() { char *message; message = malloc(14*sizeof(char)+(sizeof(int)*5)+1); sprintf(message, "The answer is %d", 42); puts(message); free(message); } 

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

 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*/ ); 

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

Не совсем, вам нужно всего несколько символов, поэтому 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 в конце – это потому, что вам нужно пространство для нулевого терминатора строк.

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

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

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

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

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

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

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

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

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

 #include  #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; } 

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

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

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

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

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

Это вычисляет количество цифр – добавляет их к обоим из них для учета терминатора, если выделяет пространство для строки с нулевым завершением.

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