Как инициализировать структуру с помощью гибкого элемента массива
У меня есть следующая структура
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
.