Создайте массив символов целого числа, используя цифры в качестве размера

Я пытаюсь создать массив символов в C, чтобы заполнить его цифрами int, но int может иметь любое количество цифр.

Я использую созданную функцию с именем getDigits(int num), которая возвращает количество цифр, которое имеет int.

char buffer[getDigits(number)] = "";
snprintf(buffer, sizeof(buffer),"%d",number);

но когда я компилирую с помощью gcc, он возвращает:

error: variable-sized object may not be initialized

Я пробовал все. Когда я объявляю его как char fileSizeStr[5] = "";, он работает. Я вижу, что проблема возникает, когда я пытаюсь динамически объявить размер буфера, но я действительно хотел бы знать, есть ли способ добиться этого.


person anairinac    schedule 07.06.2013    source источник
comment
используйте компилятор c99 или используйте malloc   -  person BLUEPIXY    schedule 07.06.2013
comment
Попробуйте удалить файл = "". snprintf заранее не волнует, что находится в его выходном буфере.   -  person zwol    schedule 07.06.2013
comment
gcc prog.c -std=c99 или заменить новую версию gcc   -  person BLUEPIXY    schedule 07.06.2013
comment
Спасибо, использование malloc сработало отлично!   -  person anairinac    schedule 07.06.2013
comment
@Zack Я пробовал это раньше и использовал sprintf. Но на данный момент работало только объявление буфера типа char fileSizeStr[5] = ""; или использование malloc.   -  person anairinac    schedule 07.06.2013
comment
@Zack Напишите ответ ... Это шутка, что здесь будет предложено malloc.   -  person autistic    schedule 07.06.2013
comment
@anairinac Что именно произошло, когда вы удалили = ""?   -  person zwol    schedule 07.06.2013
comment
Давайте посмотрим, может ли кто-нибудь придумать лучший ответ, чем то, что я дал...   -  person autistic    schedule 09.06.2013
comment
@ Зак, я действительно не понимаю, почему это не работало раньше, но теперь твое решение работает. Без инициализации и изменения объявления буфера на char buffer[getDigits(number)+1]; он вернул это: Kvothe cripto-tdes-actual # ./zack 27 -> Number: 27. Я изменю свой выбранный ответ на одно предоставленное поведение @undefined, потому что оно использует ваше, оно наиболее полное, дало альтернативные getDigits (включая макрос), дало довольно подробное объяснение того, почему использование malloc в этом случае было ненужным, и рекомендуется избегать приведения типов и умножения на 1.   -  person anairinac    schedule 10.06.2013


Ответы (5)


Проблема именно в том, о чем говорит вам ваш компилятор; вам не разрешено инициализировать VLA. Зак дал очевидное решение в комментариях: удалить инициализацию. В этом ответе вы найдете рабочие примеры, некоторые из которых разрешают инициализацию, а другие — нет. Вы найдете больше информации об этом в комментариях. Следующие примеры упорядочены от наиболее разумного (IMHO) к наименее разумному (который включает использование malloc) для выделения памяти для последовательностей десятичных цифр, представляющих числа.


Я предлагаю использовать тот же трюк, чтобы определить, сколько байтов необходимо для хранения значения int в виде десятичных цифр, которые вы использовали бы для восьмеричного: разделите общее количество битов в int на 3 и добавьте для любого знака и окончания NUL. digit_count может быть записан как макрос препроцессора следующим образом:

#include <limits.h>
#include <stddef.h>
#include <stdio.h>

#define digit_count(num) (1                                /* sign            */ \
                        + sizeof (num) * CHAR_BIT / 3      /* digits          */ \
                        + (sizeof (num) * CHAR_BIT % 3 > 0)/* remaining digit */ \
                        + 1)                               /* NUL terminator  */

int main(void) {
    short short_number = -32767;
    int int_number = 32767;
    char short_buffer[digit_count(short_number)] = { 0 }; /* initialisation permitted here */
    char int_buffer[digit_count(int_number)];
    sprintf(short_buffer, "%d", short_number);
    sprintf(int_buffer, "%d", int_number);
}

Как вы можете видеть, одно большое преимущество здесь заключается в том, что digit_count можно использовать для любого типа целого числа без модификации: char, short, int, long, long long и соответствующих типов unsigned.

Один небольшой недостаток по сравнению с этим заключается в том, что вы тратите несколько байтов памяти, особенно для небольших значений, таких как 1. Во многих случаях простота этого решения более чем компенсирует это; Код, необходимый для подсчета десятичных цифр во время выполнения, будет занимать больше места в памяти, чем тратится здесь.


Если вы готовы отказаться от простоты и универсальности приведенного выше кода и действительно хотите подсчитать количество десятичных цифр, применим совет Зака: удалите инициализацию. Вот пример:

#include <stddef.h>
#include <stdio.h>

size_t digit_count(int num) {
    return snprintf(NULL, 0, "%d", num) + 1;
}

int main(void) {
    int number = 32767;
    char buffer[digit_count(number)]; /* Erroneous initialisation removed as per Zacks advice */
    sprintf(buffer, "%d", number);
}

В ответ на рекомендации malloc: Наименее ужасный способ решить эту проблему — избегать ненужного кода (например, вызовов malloc и позже free). Если вам не нужно возвращать объект из функции, не используйте malloc! В противном случае рассмотрите возможность сохранения в буфере, предоставленном вызывающей стороной (через аргументы), чтобы вызывающая сторона могла выбрать, какой тип хранилища использовать. Очень редко это не является подходящей альтернативой использованию malloc.

Однако, если вы решите использовать для этого malloc и free, делайте это наименее ужасным способом. Избегайте приведения типов к возвращаемому значению malloc и умножения на sizeof (char) (которое всегда равно 1). Следующий код является примером. Используйте любой из вышеперечисленных методов для расчета длины:

char *buffer = malloc(digit_count(number)); /* Initialisation of malloc bytes not possible */
sprintf(buffer, "%d", number);

... и не забудьте free(buffer);, когда закончите.

person autistic    schedule 07.06.2013

попробуйте что-то вроде:

 char* buffer =(char *)malloc(getDigits(number)*sizeof(char));

malloc и calloc используются для динамического выделения памяти.

person Rafael Azevedo    schedule 07.06.2013
comment
1. Не используйте malloc. 2. Нет смысла умножать на sizeof (char), потому что sizeof (char) всегда 1 в C. - person autistic; 07.06.2013
comment
хм, интересно. Судя по тому, что я только что искал, в C нам действительно не нужно делать приведения типов в этом случае. Но программы на C++ мы делаем.. Спасибо за информацию! - person Rafael Azevedo; 07.06.2013

ниже может помочь

char* buffer;
buffer = (char*)malloc(number * sizeof(char));
person NonStatic    schedule 07.06.2013
comment
1. Не используйте malloc. 2. Нет смысла умножать на sizeof (char), потому что sizeof (char) всегда 1 в C. - person autistic; 07.06.2013
comment
почему бы не использовать malloc? Я думаю, что есть единственный способ выделить память в старом C. - person NonStatic; 07.06.2013
comment
@NigelHarper, я думаю, что я написал приведение возвращаемого значения. - person NonStatic; 07.06.2013
comment
@mmsc Да, вы возвращаете возвращаемое значение malloc, и вы не должны этого делать по причинам, объясненным в ответе, на который я дал ссылку. - person Nigel Harper; 07.06.2013

За мои деньги есть одно решение, о котором не упоминалось, но которое на самом деле проще, чем любое из вышеперечисленных. Существует комбинированная версия sprintf с выделением памяти под названием asprintf, доступная в Linux и большинстве вариантов BSD. Он определяет необходимый размер, выделяет память и возвращает заполненную строку в первый аргумент.

char * a;
asprintf(&a, "%d", 132);
// use a
free(a);

Использование массива, выделенного в стеке, безусловно, устраняет необходимость в свободном, но это полностью устраняет необходимость когда-либо отдельно вычислять размер.

person Tom Scogland    schedule 14.06.2013
comment
примечание: в системах без asprintf, используя C99, легко свернуть свой собственный, используя два вызова snprintf - person M.M; 18.12.2015

Вам нужно будет использовать malloc для выделения динамического объема памяти.

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

person John    schedule 07.06.2013
comment
Спасибо! Не могу поверить, что совсем забыл об использовании malloc. - person anairinac; 07.06.2013