Как инициализировать структуру с гибким членом массива - PullRequest
19 голосов
/ 31 декабря 2011

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

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?!

Ответы [ 3 ]

9 голосов
/ 31 декабря 2011

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

5 голосов
/ 31 декабря 2011

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

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

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

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

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

Итак, your_var.p имеет тип "person". Вы также можете использовать макрос для определения вашего инициализатора, поэтому вы можете написать строку только один раз:

#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[]. Или используйте динамическое распределение;)

2 голосов
/ 31 декабря 2011

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

person p = { 10, 'x' };

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

.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...