Действительно ли нужны члены гибкого массива?
Структура с гибким членом массива, по-видимому, не предназначена для объявления, а скорее используется вместе с указателем на эту структуру. При объявлении члена гибкого массива должен быть хотя бы один другой член, и член гибкого массива должен быть последним членом в этой структуре.
Допустим, у меня есть такой, который выглядит так:
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);
2 ответа
Указатели не являются массивами. Основные причины выбора того, что использовать, такие же, как и всегда, с массивами и указателями. В особом случае гибких элементов массива, вот несколько причин, по которым вы можете предпочесть их указателю:
Снижение требований к хранению. Указатель увеличит вашу структуру (как правило) на 4 или 8 байт, и вы потратите гораздо больше ресурсов, если выделите хранилище, на которое указывает указатель, отдельно, а не одним вызовом
malloc
,Повышение эффективности доступа. Гибкий элемент массива расположен с постоянным смещением от основания структуры. Указатель требует отдельного разыменования. Это влияет как на количество инструкций, необходимых для доступа к нему, так и на регистрацию давления.
Атомность распределения успеха / неудачи. Если вы выделите структуру и выделите для нее хранилище, которое будет указывать как два отдельных шага, ваш код для очистки в случаях сбоя будет гораздо более уродливым, поскольку у вас есть случай, когда один был успешным, а другой - неудачным. Этого можно избежать с помощью некоторой арифметики указателей, чтобы вырезать оба из одного и того же
malloc
запрос, но легко ошибиться в логике и вызвать UB из-за проблем с выравниванием.Избегать необходимости глубокого копирования. Если вы используете гибкий указатель вместо указателя, вы можете просто использовать memcpy (не присваивать, так как присваивание не может знать длину гибкого массива), чтобы скопировать структуру, вместо того, чтобы копировать данные, на которые указывает указатель, и исправить указатель. в новом экземпляре.
Избегать необходимости глубокого освобождения. Это очень удобно и чисто, чтобы иметь возможность просто
free
один объект, а не необходимостьfree
указывает на данные тоже. Это также может быть достигнуто с помощьюmalloc
"Подход, упомянутый выше, конечно, но гибкие массивы делают его проще и менее подвержен ошибкам.Конечно, еще много причин...
Эти понятия определенно не нужны, как вы указали сами.
Различия между этими двумя примерами состоят в том, где ваши данные находятся в памяти.
В первом примере с гибким массивом ваши метаданные и сам массив находятся в одном блоке памяти и могут быть перемещены как один блок (указатель), если это необходимо.
Во втором примере ваши метаданные находятся в стеке, а ваш массив находится где-то в куче. Чтобы переместить / скопировать его, теперь вам нужно переместить два блока памяти и обновить указатель в структуре метаданных.
Как правило, массивы гибких размеров используются, когда вам нужно разместить массив и его метаданные пространственно вместе в памяти.
Примером, где это определенно полезно, является, например, размещение массива с его метаданными в файле - у вас есть только один непрерывный блок памяти, и каждый раз, когда вы его загружаете, он (скорее всего) будет помещаться в другое место вашей виртуальной машины.,