Инициализация структуры с массивом переменной длины в C

Кто-нибудь знает, есть ли способ инициализировать структуру, содержащую массив переменной длины, без предварительной инициализации массива в отдельной переменной (и без использования malloc)?

Моя структура выглядит так:

struct my_struct {
    int *values;
    int size;
}

На данный момент в моем коде у меня есть это:

void my_function (int size) {
    int values[size];
    struct my_struct mystr = {
        .values = values,
        .size = size
    };
    ...
}

(Сначала инициализируется массив, затем структура. Это работает, но выглядит неудобно объявлять отдельную переменную для массива.)

Вероятно, это тоже сработает:

void my_function (int size) {
    struct my_struct mystr = { 
        .values = calloc (size, sizeof (int)),
        .size = size
    };
    ...
}

(но я не хочу использовать mallocs)

Но то, что я хотел бы написать, это что-то вроде:

void my_function (int size) {
    struct my_struct mystr = { 
        .values = (int[size]){},
        .size = size
    };
    ...
}

Есть идеи?


person FClad    schedule 22.07.2016    source источник
comment
Вы хотите иметь массив, не выделяя для него место?   -  person GMichael    schedule 22.07.2016
comment
Я знаю, что мне нужно выделить место для массива, но я хотел бы сделать это без использования промежуточной переменной, такой как int values[size];.   -  person FClad    schedule 22.07.2016
comment
Что не так с `.values ​​= calloc (size, sizeof (int)),`?   -  person GMichael    schedule 22.07.2016
comment
Динамическое выделение памяти происходит медленнее и, если вы не будете осторожны, может привести к утечкам памяти. Поэтому я стараюсь избегать этого, если в этом нет крайней необходимости.   -  person FClad    schedule 22.07.2016
comment
Поэтому инициализируйте .values значением NULL и укажите реальное значение при выделении памяти.   -  person GMichael    schedule 22.07.2016
comment
... инициализировать структуру, содержащую массив переменной длины int * values; — это указатель на int, а не массив и даже не VLA.   -  person alk    schedule 22.07.2016
comment
Динамическое выделение памяти происходит медленнее... Если вы задали этот вопрос, это недопустимо. С технической точки зрения стандарт C не содержит таких требований; VLA могут быть реализованы внутри с использованием malloc для экономии драгоценного пространства в стеке. На самом деле это не должно иметь существенного значения, если вы не усложняете расположение кеша. Для начала прекратите использовать указатель для ссылки на другой фрагмент памяти. Вместо этого используйте гибкий элемент массива.   -  person autistic    schedule 22.07.2016


Ответы (2)


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

int values[size];
struct my_struct mystr = {
    .values = values,
    .size = size
};
return mystr;

Это не сработает, так как время жизни values заканчивается, когда вы возвращаетесь. То же самое применимо, если вы пытаетесь сохранить mystr в значении, на которое указывает параметр вашей функции.

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


Ответ на ваш вопрос: это зависит от ситуации.

Вы можете быть уверены, что size мало? Иначе ваш стек переполнится в int values[size]. Он маленький и предсказуемый? Придерживайтесь своего первого решения. Если он может быть большим или зависеть от пользовательского ввода, обязательно используйте malloc.

Вы каким-то образом возвращаете или сохраняете постоянный указатель на свою структуру или значения? Используйте malloc (см. мое первое замечание).

Кроме того, вы также можете использовать взлом структуры, но тогда вам придется malloc mystr полностью тем не мение.


Еще одна вещь, вы написали:

(Сначала инициализируется массив, затем структура. Это работает, но выглядит неудобно объявлять отдельную переменную для массива.)

Я не уверен, что вы имеете в виду, но int * — это всего лишь sizeof(intptr_t), независимо от размера массива. Таким образом, вы не выделяете вдвое больше памяти для 1 массива, если вы об этом думаете.

person elslooo    schedule 22.07.2016
comment
sizeof (int *) не обязательно равно sizeof (intptr_t). Я думаю, вы упустили суть; OP не беспокоится о производительности или использовании памяти в этом вопросе. Он/она хочет быть более выразительным... Эти два понятия идут ортогонально. - person autistic; 22.07.2016

Инициализаторы — это безымянные объекты, инициализируемые списком инициализаторов. Вне тела функции объект имеет статическую продолжительность хранения. Так что можно использовать адрес такого объекта. С небольшой помощью вариативных макросов вы можете попробовать →

 #include <stdio.h>

 struct test {
   int count;
   int *values;
 } test[] = {
 #define init(...) { .count=sizeof( (int[]) {__VA_ARGS__} )/sizeof(int), .values=(int *)&(int []){__VA_ARGS__} }
              init(0,1,2,3,4),
              init(2,4,6,8),
              init(1,3),
              init(42)
            };
 #define test_size ((int) (sizeof test/sizeof *test))

 int main(void)
 {
   for(int array=0; array<test_size; ++array) {
     printf("array %d (%d) : [ ", array+1, test[array].count);
     for(int i=0; i<test[array].count; ++i)
       printf("%d ", test[array].values[i]);
     puts("]");
   }
   return 0;
 }
person Picodev    schedule 22.07.2016