Как инициализировать структуру с помощью гибкого элемента массива

У меня есть следующая структура

typedef struct _person {
    int age;
    char sex;
    char name[];
}person;

Я сделал базовый поиск в Интернете (но безуспешно) о том, как создать экземпляр и инициализировать структуру с помощью гибкого элемента массива без использования malloc().

Например: для обычных структур, таких как

struct a {
    int age; 
    int sex;
};

Мы можем создать экземпляр struct a и инициализировать его как

struct a p1 = {10, 'm'};

Но для структур с гибким массивом в нем (например, _person, как упоминалось выше), как мы можем создать экземпляр и инициализировать, как мы делаем это для обычного structures?

Возможно ли это? Если да, то как мы передаем размер массива во время инициализации и фактическое значение, которое нужно инициализировать?

(или)

Верно ли, что единственный способ создать структуру с помощью гибкого массива - использовать malloc(), как указано в спецификации C99 - 6.7.2.1 Structure and union specifiers - point #17?!

Ответы

Ответ 1

Нет, гибкие массивы всегда должны быть назначены вручную. Но вы можете использовать calloc для инициализации гибкой части и составного литерала для инициализации фиксированной части. Я бы обернул это в функцию распределения inline следующим образом:

typedef struct person {
  unsigned age;
  char sex;
  size_t size;
  char name[];
} person;

inline
person* alloc_person(int a, char s, size_t n) {
  person * ret = calloc(sizeof(person) + n, 1);
  if (ret) memcpy(ret,
                  &(person const){ .age = a, .sex = s, .size = n},
                  sizeof(person));
  return ret;
}

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

Если вам не нужно поле size, поскольку я включил его здесь, макроса было бы достаточно. Только, чтобы выполнить возврат calloc, не удастся проверить memcpy. Во всех системах, которые я запрограммировал до сих пор, это будет отменено относительно красиво. Как правило, я считаю, что возврат malloc имеет второстепенное значение, но мнения в значительной степени зависят от этого вопроса.

Это может быть (в этом специальном случае) предоставить больше возможностей оптимизатору для интеграции кода в окружении:

#define ALLOC_PERSON(A,  S,  N)                                 \
((person*)memcpy(calloc(sizeof(person) + (N), 1),               \
                 &(person const){ .age = (A), .sex = (S) },     \
                 sizeof(person)))

Изменить: Дело в том, что это может быть лучше, чем функция, когда A и S являются константами времени компиляции. В этом случае составной литерал, так как он const квалифицирован, может быть назначен статически, и его инициализация может быть выполнена во время компиляции. Кроме того, если в коде появится несколько распределений с одинаковыми значениями, компилятору будет разрешено реализовать только одну копию этого составного литерала.

Ответ 2

Есть несколько трюков, которые вы можете использовать. Это зависит от вашего конкретного приложения.

Если вы хотите инициализировать одну переменную, вы можете определить структуру правильного размера:

   struct  {
        int age;
        char sex;
        char name[sizeof("THE_NAME")];
    } your_variable = { 55, 'M', "THE_NAME" };

Проблема заключается в том, что для интерпретации переменной нужно использовать функцию "person" (например, "* (person *) (& your_variable)". Но вы можете использовать объединенный союз, чтобы этого избежать:

union {
 struct { ..., char name[sizeof("THE_NAME")]; } x;
 person p;
} your_var = { 55, 'M', "THE_NAME" };

Итак, ваш_var.p имеет тип "человек". Вы также можете использовать макрос, чтобы определить свой инициализатор, поэтому вы можете написать строку только один раз:

#define INIVAR(x_, age_, sex_ ,s_) \
   union {\
     struct { ..., char name[sizeof(s_)]; } x;\
     person p;\
    } x_ = { (age_), (sex_), (s_) }

INIVAR(your_var, 55, 'M', "THE NAME");

Другая проблема заключается в том, что этот трюк не подходит для создания массива "человек". Проблема с массивами состоит в том, что все элементы должны иметь одинаковый размер. В этом случае безопаснее использовать const char * вместо char[]. Или используйте динамическое распределение;)

Ответ 3

Тип структуры с гибким элементом массива может быть обработан так, как если бы элемент гибкого массива был опущен, поэтому вы можете инициализировать структуру, подобную этой.

person p = { 10, 'x' };

Однако нет элементов распределенного гибкого массива, и любая попытка доступа к элементу гибкого массива или форматирования указателя на один за его пределами недействительна. Единственный способ создать экземпляр структуры с гибким членом массива, который на самом деле имеет элементы в этом массиве, - это динамически выделять для него память, например, с помощью malloc.