Spec-Zone.ru › GCC 14

6.18 Массивы нулевой длины

Объявление массивов нулевой длины разрешено в GNU C как расширение. Массив нулевой длины может быть полезен в качестве последнего элемента структуры, которая фактически является заголовком для объекта переменной длины:

struct line {
  int length;
  char contents[0];
};

struct line *thisline = (struct line *)
  malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

В этом примере, thisline->contents является массивом char, который может хранить до thisline->length байт.

Хотя размер массива нулевой длины равен нулю, член массива этого типа может увеличить размер содержащего типа в результате заполнения хвоста. Смещение члена массива нулевой длины от начала содержащей структуры такое же, как смещение массива с одним или несколькими элементами того же типа. Выравнивание массива нулевой длины такое же, как выравнивание его элементов.

Объявление массивов нулевой длины в других контекстах, включая внутренние члены объектов структуры или объекты, не являющиеся членами, не рекомендуется. Доступ к элементам массивов нулевой длины, объявленных в таких контекстах, не определён и может быть диагностирован.

В отсутствие расширения для массивов нулевой длины в ISO C90 массив contents в примере выше обычно объявлялся с одним элементом. В отличие от массива нулевой длины, который влияет только на размер содержащей структуры в целях выравнивания, массив с одним элементом всегда занимает по меньшей мере столько же места, сколько и один объект указанного типа. Хотя использование массивов с одним элементом таким образом не рекомендуется, GCC обрабатывает доступ к элементам массива с одним элементом в конце аналогично массивам нулевой длины.

Предпочтительный механизм для объявления типов переменной длины, таких как struct line выше, — это гибкий массив C99, с немного отличающейся синтаксисом и семантикой:

  • Гибкие члены массива записываются как contents[] без 0.
  • Гибкие члены массива имеют неполный тип, и поэтому оператор sizeof не может быть применён. В качестве особенности первоначальной реализации массивов нулевой длины, sizeof равно нулю.
  • Гибкие члены массива могут появляться только как последний член struct, который в остальном не пуст.
  • Структура, содержащая гибкий член массива, или объединение, содержащее такую структуру (возможно, рекурсивно), не может быть членом структуры или элементом массива. (Однако эти использования разрешены GCC в качестве расширений, см. подробности ниже.)

Расширение GCC допускает структуру, содержащую гибкий массив C99, или объединение, содержащее такую структуру (возможно, рекурсивно), в качестве члена структуры.

Существуют две ситуации:

  • Структура, содержащая гибкий массив C99, или объединение, содержащее такую структуру, является последним полем другой структуры, например:
    struct flex  { int length; char data[]; };
    union union_flex { int others; struct flex f; };
    
    struct out_flex_struct { int m; struct flex flex_data; };
    struct out_flex_union { int n; union union_flex flex_data; };

    В приведенном выше примере out_flex_struct.flex_data.data[] и out_flex_union.flex_data.f.data[] также считаются гибкими массивами.

  • Структура, содержащая гибкий массив C99, или объединение, содержащее такую структуру, не является последним полем другой структуры, например:
    struct flex  { int length; char data[]; };
    
    struct mid_flex { int m; struct flex flex_data; int n; };

    В приведенном выше примере доступ к элементу массива mid_flex.flex_data.data[] может иметь неопределённое поведение. Компиляторы не обрабатывают такой случай последовательно. Любой код, полагающийся на этот случай, должен быть изменён, чтобы гарантировать, что гибкие члены массива оказываются только в конце структур.

    Пожалуйста, используйте опцию предупреждения -Wflex-array-member-not-at-end, чтобы идентифицировать все такие случаи в исходном коде и изменить их. Это расширение теперь устарело.

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

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-14.2.0/gcc/Zero-Length.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API