Использование членов гибкого массива в C плохая практика?

c arrays data-structures flexible-array-member

53733 просмотра

7 ответа

Недавно я читал, что использование гибких элементов массива в C было плохой практикой разработки программного обеспечения. Однако это утверждение не было подкреплено никакими аргументами. Это признанный факт?

( Гибкие члены массива - это функция C, представленная в C99, посредством которой можно объявить последний элемент как массив неопределенного размера. Например:)

struct header {
    size_t len;
    unsigned char data[];
};
Источник Размещён: 17.06.2019 07:49

Ответы (7)


24 плюса

Это общепризнанный «факт», что использование goto - плохая практика разработки программного обеспечения. Это не делает это правдой. Есть моменты, когда goto полезен, особенно при обработке очистки и при переносе с ассемблера.

Члены гибкого массива, как мне кажется, имеют одно основное применение, вне моей головы, это отображение устаревших форматов данных, таких как форматы шаблонов окон в RiscOS. Они были бы в высшей степени полезны для этого около 15 лет назад, и я уверен, что есть еще люди, которые занимаются такими вещами, которые находят их полезными.

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

Автор: Airsource Ltd Размещён: 29.10.2008 02:36

18 плюса

ПОЖАЛУЙСТА, ВНИМАТЕЛЬНО ПРОЧТИТЕ КОММЕНТАРИИ НИЖЕ ОТВЕТА

По мере продвижения C-стандартизации больше нет причин использовать [1].

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

Дело в том, что вы всегда можете использовать следующую идиому:

struct header {
  size_t len;
  unsigned char data[1];
};

Это полностью переносимо. Тогда вы можете принять во внимание 1 при выделении памяти для n элементов в массиве data:

ptr = malloc(sizeof(struct header) + (n-1));

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

Автор: Remo.D Размещён: 29.10.2008 02:36

10 плюса

Вы имели в виду...

struct header
{
 size_t len;
 unsigned char data[];
}; 

В Си это обычная идиома. Я думаю, что многие компиляторы также принимают:

  unsigned char data[0];

Да, это опасно, но опять же, это действительно не более опасно, чем обычные массивы C - то есть, ОЧЕНЬ опасно ;-). Используйте его с осторожностью и только в тех случаях, когда вам действительно нужен массив неизвестного размера. Убедитесь, что вы malloc и правильно освободите память, используя что-то вроде: -

  foo = malloc(sizeof(header) + N * sizeof(data[0]));
  foo->len = N;

Альтернатива - сделать данные просто указателями на элементы. Затем вы можете realloc () данных до правильного размера, как требуется.

  struct header
    {
     size_t len;
     unsigned char *data;
    }; 

Конечно, если вы спрашиваете о C ++, любой из них будет плохой практикой. Тогда вместо этого вы обычно используете векторы STL.

Автор: Roddy Размещён: 29.10.2008 02:36

6 плюса

Я видел что-то вроде этого: из интерфейса C и реализации.

  struct header {
    size_t len;
    unsigned char *data;
};

   struct header *p;
   p = malloc(sizeof(*p) + len + 1 );
   p->data = (unsigned char*) (p + 1 );  // memory after p is mine! 

Примечание: данные не обязательно должны быть последними.

Автор: Nyan Размещён: 01.06.2010 09:30

6 плюса

Нет, использование членов гибкого массива в C не является плохой практикой.

Эта функция языка была впервые стандартизирована в ISO C99, 6.7.2.1 (16). Для действующего стандарта ISO C11 он указан в разделе 6.7.2.1 (18).

Вы можете использовать их так:

struct Header {
    size_t d;
    long v[];
};
typedef struct Header Header;
size_t n = 123; // can dynamically change during program execution
// ...
Header *h = malloc(sizeof(Header) + sizeof(long[n]));
h->n = n;

Кроме того, вы можете выделить его следующим образом:

Header *h = malloc(sizeof *h + n * sizeof h->v[0]);

Обратите внимание, что это sizeof(Header)включает в себя байты заполнения, поэтому следующее распределение некорректно и может привести к переполнению буфера:

Header *h = malloc(sizeof(size_t) + sizeof(long[n])); // invalid!

Структура с элементами гибкого массива уменьшает количество выделений для него на 1/2, т. Е. Вместо 2 выделений для одного объекта структуры вам нужно всего лишь 1. То есть меньше усилий и меньше памяти, занимаемой накладными расходами на распределение памяти. Кроме того, вы сохраняете хранилище для одного дополнительного указателя. Таким образом, если вам нужно выделить большое количество таких экземпляров структуры, вы заметно улучшите время выполнения и использование памяти вашей программой (с помощью постоянного фактора).

В отличие от этого, использование нестандартизированных конструкций для гибких элементов массива, которые приводят к неопределенному поведению (например, в long v[0];или long v[1];), очевидно, является плохой практикой. Таким образом, как и любое неопределенное поведение, этого следует избегать.

С момента выпуска ISO C99 в 1999 году, почти 20 лет назад, стремление к совместимости с ISO C89 является слабым аргументом.

Автор: maxschlepzig Размещён: 16.09.2017 08:37

4 плюса

В качестве примечания, для совместимости с C89 такая структура должна быть выделена следующим образом:

struct header *my_header
  = malloc(offsetof(struct header, data) + n * sizeof my_header->data);

Или с макросами:

#define FLEXIBLE_SIZE SIZE_MAX /* or whatever maximum length for an array */
#define SIZEOF_FLEXIBLE(type, member, length) \
  ( offsetof(type, member) + (length) * sizeof ((type *)0)->member[0] )

struct header {
  size_t len;
  unsigned char data[FLEXIBLE_SIZE];
};

...

size_t n = 123;
struct header *my_header = malloc(SIZEOF_FLEXIBLE(struct header, data, n));

Установка FLEXIBLE_SIZE в SIZE_MAX почти гарантирует, что это не удастся:

struct header *my_header = malloc(sizeof *my_header);
Автор: diapir Размещён: 20.05.2009 02:26

4 плюса

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

Например, если вы запускаете функцию:

void test(void) {
  struct header;
  char *p = &header.data[0];

  ...
}

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

Например, если я определю:

struct header2 {
  int len;
  char data[MAXLEN]; /* MAXLEN some appropriately large number */
}

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

struct header2 inst1 = inst2;

Или если они определены как указатели:

struct header2 *inst1 = *inst2;

Однако это не сработает, поскольку массив переменных dataне копируется. То, что вы хотите, это динамически распределить размер структуры по размеру и скопировать в массив memcpyили эквивалентный.

Аналогично, написание функции, которая принимает структуру, не будет работать, так как аргументы в вызовах функций, опять же, копируются по значению, и, таким образом, то, что вы получите, вероятно, будет только первым элементом вашего массива data.

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

Автор: wds Размещён: 03.03.2014 09:31
Вопросы из категории :
32x32