Действительно ли необходимы гибкие элементы массива?

Структура с гибким элементом массива, по-видимому, не предназначена для объявления, а скорее используется вместе с указателем на эту структуру. При объявлении гибкого члена массива должен быть хотя бы один другой член, и этот гибкий член массива должен быть последним членом в этой структуре.

Допустим, у меня есть такой:

struct example{
    int n;
    int flm[]; 
}

Затем, чтобы использовать его, мне нужно будет объявить указатель и использовать malloc для резервирования памяти для содержимого структуры.

struct example *ptr = malloc(sizeof(struct example) + 5*sizeof(int)); 

То есть, если я хочу, чтобы мой массив flm [] содержал пять целых чисел. Затем я могу просто использовать свою структуру следующим образом:

ptr->flm[0] = 1; 

У меня вопрос, а разве я не могу использовать вместо этого указатель? Он не только был бы совместим до C99, но я мог бы использовать его с указателем на эту структуру или без него. Учитывая, что мне уже нужно использовать malloc с flm, разве я не могу это сделать?

Рассмотрим это новое определение структуры примера;

struct example{
    int n; 
    int *notflm; 
}

struct example test = {4, malloc(sizeof(int) * 5)}; 

Я бы даже смог использовать замену так же, как гибкий член массива:

Это тоже сработает? (При условии, что приведенное выше определение примера с notflm)

struct example test; 
test.n = 4; 
notflm = malloc(sizeof(int) * 5); 

person Theo Chronic    schedule 01.08.2013    source источник
comment
возможный дубликат элементов гибкого массива в C - плохо?. Гибкие массивы являются частью C99; указатели не являются и могут иметь проблемы с переносимостью.   -  person Ted Hopp    schedule 01.08.2013
comment
Основное отличие состоит в том, что гибкие элементы массива позволяют разместить всю структуру в одном непрерывном блоке. Если вы хотите скопировать структуру, просто вычислите ее размер и сделайте одну копию. В то время как с указателем вы должны скопировать саму структуру, но затем также скопировать внутренний массив.   -  person Drew McGowen    schedule 01.08.2013
comment
@TedHopp, каким мыслимым образом вы могли бы всерьез сказать, что указатели не являются частью C99?   -  person This isn't my real name    schedule 05.08.2013
comment
@ElchononEdelson - Это было плохо сформулировано и должно интерпретироваться в очень узком контексте. Я имел в виду, что использование указателей в конце struct в качестве способа реализации гибких массивов не является частью стандарта C99 (и может иметь проблемы с переносимостью, в основном из-за скрытого заполнения для выравнивания массива / указателя).   -  person Ted Hopp    schedule 05.08.2013
comment
@TedHopp Понятно. Да, использование указателя в конце struct как способа реализации гибких массивов действительно было бы глупо. (Однако использование указателя в качестве последнего элемента struct, указывающего на отдельно выделенную память, совершенно разумно и происходит постоянно. Это не то же самое, что гибкие массивы.)   -  person This isn't my real name    schedule 05.08.2013
comment
@ElchononEdelson - Это может быть глупо (и опасно), но это делается слишком часто (что, как я думаю, послужило причиной добавления в стандарт C99). Соблазн игнорировать опасности состоит в том, что это позволяет избежать второго выделения памяти (и необходимой очистки), а также может устранить косвенное обращение к одному адресу при каждом доступе. К сожалению, этот трюк работает на многих архитектурах, поэтому неопытные программисты думают, что он просто работает.   -  person Ted Hopp    schedule 05.08.2013
comment
Мы говорим здесь о разных вещах? Чтобы использовать указатель в конце структуры в качестве гибкого члена массива, вам потребуется примерно struct foo {int bar; int *ary;} *my; my=malloc(sizeof(struct foo)+10*sizeof(int)); my.ary=(int*)(my+1);. Это, конечно, глубоко глупо. Другое дело - позволить последнему элементу структуры быть указателем на другое место, правильно инициализированным адресом правильно выделенного фрагмента памяти. Это не структура с гибким элементом массива, это просто другой метод со своим собственным набором проблем.   -  person This isn't my real name    schedule 05.08.2013


Ответы (2)


Указатели - это не массивы. Основные причины выбора того, что использовать, те же, что и всегда с массивами, а не указателями. В особом случае гибких элементов массива вот несколько причин, по которым вы можете предпочесть их указателю:

  • Снижение требований к хранилищу. Указатель увеличит вашу структуру (обычно) на 4 или 8 байтов, и вы потратите гораздо больше на накладные расходы, если выделите указанное хранилище отдельно, а не с одним вызовом malloc.

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

  • Атомарность успешного / неудачного размещения. Если вы выделите структуру и выделите для нее хранилище, чтобы указать на два отдельных шага, ваш код для очистки в случаях сбоя будет намного уродливее, поскольку у вас есть случай, когда один был успешным, а другой - нет. Этого можно избежать с помощью некоторой арифметики с указателями для выделения обоих из одного и того же malloc запроса, но легко ошибиться в логике и вызвать UB из-за проблем с выравниванием.

  • Отсутствие необходимости в глубоком копировании. Если вы используете гибкий массив вместо указателя, вы можете просто memcpy (не присваивать, поскольку присваивание не может знать длину гибкого массива), чтобы скопировать структуру, вместо того, чтобы также копировать указанные данные и исправить указатель в новом экземпляре.

  • Избегайте необходимости в глубоком освобождении. Очень удобно и чисто иметь возможность просто free один объект вместо того, чтобы free указывать на данные тоже. Этого также можно достичь с помощью упомянутого выше подхода «разделить один malloc», но гибкие массивы делают это проще и менее подвержены ошибкам.

  • Наверняка еще много причин ...

person R.. GitHub STOP HELPING ICE    schedule 01.08.2013
comment
+1 хороший список причин, я искал хорошее резюме преимуществ этого ответа, но я ничего не нашел действительно здорово, я также собираюсь дать ссылку на этот ответ, b / c это лучше, чем все, что я нашел до сих пор. - person Shafik Yaghmour; 26.11.2013

Эти концепции определенно не нужны, как вы сами отметили.

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

В первом примере с гибким массивом ваши метаданные и сам массив находятся в одном блоке памяти и при необходимости могут быть перемещены как один блок (указатель).

Во втором примере ваши метаданные находятся в стеке, а ваш массив находится где-то в куче. Чтобы переместить / скопировать его, вам теперь нужно переместить два блока памяти и обновить указатель в структуре метаданных.

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

Пример, в котором это определенно полезно, - это, например, размещение массива с его метаданными в файле - у вас есть только один непрерывный блок памяти, и каждый раз, когда вы его загружаете, он (скорее всего) будет размещаться в другом месте вашей виртуальной машины. .

person Sergey L.    schedule 01.08.2013
comment
Данные массива во многих случаях могут быть расположены сразу после данных структуры. Это позволило бы использовать единственное выделение, но каждый раз, когда структура загружается, перемещается или копируется, необходимо обновлять указатель. - person supercat; 02.08.2013