6.17 Массивы нулевой длины
Массивы нулевой длины разрешены в GNU C. Они очень полезны в качестве последнего элемента структуры, которая фактически является заголовком для объекта переменной длины:
struct line {
int length;
char contents[0];
};
struct line *thisline = (struct line *)
malloc (sizeof (struct line) + this_length);
thisline->length = this_length; В ISO C90, вам пришлось бы указать contents длину 1, что означает либо потраченное пространство, либо усложнение аргумента для malloc.
В ISO C99, вы бы использовали гибкий член массива, который немного отличается по синтаксису и семантике:
- Гибкие члены массива записываются как
contents[]без0. - Гибкие члены массива имеют неполный тип, и поэтому оператор
sizeofне может быть применен. Как особенность первоначальной реализации массивов нулевой длины,sizeofвычисляется как ноль. - Гибкие члены массива могут появляться только в качестве последнего члена
struct, который в противном случае не пуст. - Структура, содержащая гибкий член массива, или объединение, содержащее такую структуру (возможно, рекурсивно), не может быть членом структуры или элементом массива. (Однако эти использования разрешены GCC в качестве расширений.)
Версии GCC до 3.0 разрешали статическую инициализацию массивов нулевой длины, как если бы они были гибкими массивами. В дополнение к тем случаям, которые были полезны, это также разрешало инициализацию в ситуациях, которые могли бы испортить последующие данные. Теперь непустая инициализация массивов нулевой длины рассматривается так же, как и в случае, когда количество элементов инициализации больше, чем вмещает массив, в том смысле, что выдается соответствующее предупреждение о «лишних элементах в массиве», и лишние элементы (все из них, в этом случае) игнорируются.
Вместо этого 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 } }; Удобство этого расширения заключается в том, что f1 имеет желаемый тип, устраняя необходимость последовательно ссылаться на f2.f1.
Это имеет симметрию с обычными статическими массивами, в том смысле, что массив неизвестного размера также записывается с [].
Конечно, это расширение имеет смысл только если дополнительные данные находятся в конце объекта верхнего уровня, так как в противном случае мы будем перезаписывать данные в последующих смещениях. Чтобы избежать излишнего усложнения и путаницы с инициализацией глубоко вложенных массивов, мы просто не разрешаем никакую непустую инициализацию, за исключением случая, когда структура является объектом верхнего уровня. Например:
struct foo { int x; int y[]; };
struct bar { struct foo z; };
struct foo a = { 1, { 2, 3, 4 } }; // Valid.
struct bar b = { { 1, { 2, 3, 4 } } }; // Invalid.
struct bar c = { { 1, { } } }; // Valid.
struct foo d[1] = { { 1 { 2, 3, 4 } } }; // Invalid.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-4.9.4/gcc/Zero-Length.html