Каковы реальные преимущества гибкого члена массива?

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

Возможный дубликат:
Гибкие члены массива в C - плохо?
Является ли это гибким элементом массива в C?

(Вини меня, если я не решил мою проблему из возможных повторяющихся вопросов выше)

В чем реальная разница между следующими двумя реализациями:

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

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

Я знаю, что размер h2 такой, как если бы гибкий элемент массива (данные) были опущены, то есть sizeof(h2) == sizeof(size_t), И я также знаю, что гибкий элемент массива может появляться только как последний элемент структуры, поэтому исходная реализация может быть более гибкой в ​​положении data,

Моя настоящая проблема заключается в том, почему C99 добавляет эту функцию? Просто потому, что sizeof(h2) не содержит реального размера данных? Я уверен, что я должен пропустить еще несколько важных моментов для этой функции. Пожалуйста, укажите это для меня.

3 ответа

Решение

Две структуры в вашем посте вообще не имеют одинаковой структуры. h1 имеет целое число и указатель на символ h2 имеет целое число и встроенный массив символов (количество элементов, определенных во время выполнения, возможно, ни одного).

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

Это имеет большое значение. Например, если вы используете h1 вам нужно позаботиться о распределении / освобождении полезной нагрузки (в дополнение к самой структуре). С h2 требуется только одно выделение / освобождение, все упаковано вместе.

Один случай использования h2 может иметь смысл, если вы общаетесь с чем-то, что ожидает сообщения в форме {length,data} пар. Вы выделяете экземпляр h2 по запросу sizeof(h2)+how many payload chars you want, заполните его, и тогда вы можете передать все это в одном write (заботясь о порядке байтов и тому подобное). Если бы вы использовали h1 вам понадобится два write звонки (если вы не хотите отправить адрес памяти данных, что обычно не имеет никакого смысла).

Так что эта функция существует, потому что это удобно. И различные (иногда непереносимые) приемы, которые использовались до этого для симуляции этой функции. Добавление его в стандарт имеет смысл.

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

Обоснование международного стандарта - Языки программирования - C §6.7.2.1 Структура и спецификаторы объединения

Существует общая идиома, известная как "взлом структуры" для создания структуры, содержащей массив переменного размера:

struct s
{
int n_items;
/* possibly other fields */
int items[1];
};

struct s *p;
size_t n, i;
/* code that sets n omitted */
p = malloc(sizeof(struct s) + (n - 1) * sizeof(int));
/* code to check for failure omitted */
p->n_items = n;
/* example usage */
for (i = 0; i < p->n_items; i++)
    p->items[i] = i;

Обоснованность этой конструкции всегда была сомнительной. В ответ на один Отчет о дефектах Комитет решил, что это было неопределенное поведение, потому что массив p->items содержит только один элемент, независимо от того, существует ли место. Была предложена альтернативная конструкция: сделать размер массива больше максимально возможного (например, используя int items[INT_MAX];), но этот подход также не определен по другим причинам.

Комитет счел, что, хотя в C89 не было никакого способа реализовать "структурный взлом", он тем не менее оказался полезным. Поэтому была введена новая функция "гибких элементов массива". Помимо пустых скобок и удаления “-1” в malloc вызов, это используется так же, как взломать структуру, но теперь это явно допустимый код.

Есть несколько ограничений на гибкие члены массива, которые гарантируют, что код, использующий их, имеет смысл. Например, должен быть хотя бы один другой член, а гибкий массив должен появляться последним. Аналогично, структуры, содержащие гибкие массивы, не могут встречаться в других структурах или в массивах. В заключение, sizeof Применительно к структуре игнорирует массив, но считает любой отступ перед ним. Это делает malloc звоните как можно проще.

Я не знаю, считается ли это важным моментом, но документы GCC указывают на это:

GCC позволяет статическую инициализацию гибких элементов массива. Это эквивалентно определению новой структуры, содержащей исходную структуру, за которой следует массив достаточного размера для размещения данных. Например, в дальнейшем f1 создается так, как если бы он был объявлен как f2.

struct f1 {
    int x; int y[];
} f1 = { 1, { 2, 3, 4 } };

struct f2 {
    struct f1 f1; int data[3];
} f2 = { { 1 }, { 2, 3, 4 } };

(взято с http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html)

Другие вопросы по тегам