Заключение в кавычки от C-std разделяет 6.7.2.1,
struct s { int n; double d[]; };
Это - действительное описание структуры. Я ищу некоторое практическое применение этого вида синтаксиса. Быть точным, как больше эта конструкция или менее мощна, чем хранение двойного* как 2-й элемент? Или этот другой случай 'you-can-do-it-in-multiple-ways''?
Arpan
FAQ по C отвечает именно на этот вопрос. Быстрый ответ заключается в том, что эта структура будет включать массив double
внутри структуры, а не указатель на массив вне структуры. В качестве краткого примера, вы можете использовать вашу структуру как в этом примере:
struct s mystruct = malloc(sizeof(struct s) + 5 * sizeof(double));
s.n = 12;
s.d[0] = 4.0;
s.d[1] = 5.0;
s.d[2] = 6.0;
s.d[3] = 7.0;
s.d[4] = 8.0;
И так далее - размер массива, о котором вы заботитесь, включается в выделение, и затем вы можете использовать его как любой массив. Обычно такой тип содержит размер как часть структуры, поскольку использование трюка +
для пропуска через массив типа s
будет обязательно осложнено этой ситуацией.
На ваш дополнительный вопрос "чем эта конструкция более или менее мощная, чем хранение [указателя] в качестве второго элемента?", она не более мощная сама по себе, но вам не нужно держать указатель рядом, так что вы сэкономите по крайней мере столько места - также при копировании структуры вы копируете массив, а не указатель на массив - тонкая разница иногда, но очень важная в другое время. 'You-can-do-it-in-multiple-ways' - это, вероятно, хорошее объяснение, но есть случаи, когда вам конкретно нужна та или иная конструкция.
Я видел, как это использовалось в Windows для строк, помеченных по их длине. Символьные данные сохраняются в памяти сразу после длины, что позволяет аккуратно хранить все вместе.
typedef struct {
SIZE_T bytes;
TCHAR chars[];
} tagged_string;
Основным преимуществом является то, что гибкий элемент массива позволяет вам выделить единственный блок памяти для массива вместе с другими данными в структуре (с указателем, вы обычно получаете два отдельно выделенных блока).
Это также полезно для данных, передаваемых несколькими сетевыми протоколами, где входящий поток определяется одинаково - целое число, определяющее длину, за которым следует такое количество единиц (обычно байтов / октетов) данных. Вы можете (как правило) использовать каламбур, чтобы наложить структуру с гибким элементом массива на буфер, заполненный такими данными, и работать с ним напрямую, вместо того, чтобы разбирать его на части, а затем работать с частями по отдельности.
Вы можете использовать его для добавления полей заголовка в динамически выделяемые массивы, наиболее распространенным из которых будет его размер:
struct int_array
{
size_t size;
int values[];
};
struct int_array *foo = malloc(sizeof *foo + 42 * sizeof *foo->values);
foo->size = 42;
...
for(size_t i = 0; i < foo->size; ++i)
foo->values[i] = i * i;
Вы можете добиться аналогичных результатов, используя вместо этого член int *
и выделяя массив отдельно, но он был бы менее эффективным как с точки зрения памяти (дополнительный указатель, управление кучей для 2-го блока памяти), так и времени выполнения (дополнительное косвенное обращение, 2-е выделение).