Spec-Zone.ru › GCC 15

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

В 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 выше, — использовать гибкий член массива из ISO C99, синтаксис и семантика которого немного отличаются:

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

Расширение GCC допускает использование структуры, содержащей гибкий член массива из ISO 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-15.3.0/gcc/Zero-Length.html

Spec-Zone.ru

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