Что произойдет, если я определяю массив 0-размера в C / C ++?

Любопытно, что на самом деле происходит, если я определяю массив int array[0]; с нулевой длиной int array[0]; в коде? GCC вообще не жалуется.

Пример программы

 #include  int main() { int arr[0]; return 0; } 

осветление

Я на самом деле пытаюсь выяснить, были ли инициализированы эти массивы нулевой длины, вместо того, чтобы указывать на переменную длину в комментариях Darhazer, оптимизированы или нет.

Это связано с тем, что я должен выпустить некоторый код в wild, поэтому я пытаюсь выяснить, должен ли я обрабатывать случаи, когда SIZE определяется как 0 , что происходит в некотором коде со статически заданным int array[SIZE];

Я был действительно удивлен, что GCC не жалуется, что привело к моему вопросу. Из полученных ответов я считаю, что отсутствие предупреждения во многом связано с поддержкой старого кода, который не был обновлен новым синтаксисом [].

Поскольку я в основном интересовался ошибкой, я помечаю ответ Лундина как правильный (сначала был Наваз, но он был не таким полным), остальные указывали на его фактическое использование для хвостовых конструкций, в то время как релевантно, точно, что я искал.

Массив не может иметь нулевой размер.

ISO 9899: 2011 6.7.6.2:

Если выражение является константным выражением, оно должно иметь значение больше нуля.

Вышеприведенный текст верен как для простого массива (параграф 1). Для VLA (массив переменной длины) поведение не определено, если значение выражения меньше или равно нулю (параграф 5). Это нормативный текст в стандарте C. Компилятору не разрешено реализовать его по-разному.

gcc -std=c99 -pedantic дает предупреждение для случая, отличного от VLA.

Обычно это запрещено.

Однако на практике в C использовались гибкие массивы .

C99 6.7.2.1, §16 : В качестве особого случая последний элемент структуры с более чем одним именованным элементом может иметь неполный тип массива; это называется гибким элементом массива.

Демонстрация:

 struct Array { size_t size; int content[]; }; 

Идея состоит в том, что вы тогда выделили бы ее так:

 void foo(size_t x) { Array* array = malloc(sizeof(size_t) + x * sizeof(int)); array->size = x; for (size_t i = 0; i != x; ++i) { array->content[i] = 0; } } 

Вы также можете использовать его статически (расширение gcc):

 Array a = { 3, { 1, 2, 3 } }; 

Это также известно как структуры с хвостовым покрытием (этот термин предшествует публикации стандарта C99) или структурного взлома (благодаря Джо Врешнигу для указания его).

Однако этот синтаксис был стандартизирован (и гарантированный эффект) только в последнее время на C99. До того, как был необходим постоянный размер.

  • 1 был переносным способом, хотя это было довольно странно
  • 0 было лучше указывать намерение, но не было законным в отношении Стандарта и поддерживалось в качестве расширения некоторыми компиляторами (включая gcc)

Однако практика заполнения хвоста основана на том, что хранилище доступно (тщательный malloc ), поэтому он не подходит для использования в стеке в целом.

В стандартном C и C ++ массив нулевого размера запрещен.

Если вы используете GCC, скомпилируйте его с параметром -pedantic . Это даст предупреждение , сказав:

zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]

В случае C ++ он дает аналогичное предупреждение.

Это совершенно незаконно и всегда было, но многие компиляторы пренебрегают сигналом ошибки. Я не уверен, почему вы хотите это сделать. Единственное, что я знаю, это вызвать ошибку времени компиляции из булева:

 char someCondition[ condition ]; 

Если condition является ложным, тогда я получаю ошибку времени компиляции. Поскольку компиляторы действительно позволяют это, я, однако, принял к использованию:

 char someCondition[ 2 * condition - 1 ]; 

Это дает размер 1 или -1, и я никогда не нашел компилятор, который бы принял размер -1.

Я добавлю, что есть целая страница онлайн-документации gcc по этому аргументу.

Некоторые цитаты:

В GNU C. разрешены массивы нулевой длины.

В ISO C90 вам нужно будет предоставить содержимое длиной 1

а также

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

так что вы могли бы

 int arr[0] = { 1 }; 

и бум 🙂

Объявления с нулевым размером массива в структурах были бы полезны, если бы они были разрешены, и если семантика была такой, что (1) они заставили бы выравнивание, но в противном случае не выделяли бы какое-либо пространство, и (2) индексирование массива будет считаться определенным поведением в где результирующий указатель будет находиться в том же блоке памяти, что и структура. Такое поведение никогда не разрешалось ни одним стандартом С, но некоторые старые компиляторы допускали его до того, как он стал стандартным для компиляторов, чтобы разрешить объявления неполного массива с пустыми скобками.

Структурный хак, как обычно реализуется с использованием массива размером 1, является изворотливым, и я не думаю, что есть какие-либо требования, которые компиляторы воздерживаются от его взлома. Например, я ожидал бы, что если компилятор увидит int a[1] , он будет в пределах своих прав рассматривать a[i] как a[0] . Если кто-то пытается обойти проблемы выравнивания структуры хака через что-то вроде

 typedef struct {
   uint32_t размер;
   uint8_t данные [4];  // Использовать четыре, чтобы избежать добавления отбрасывания размера структуры
 }

компилятор может стать умным и предположить, что размер массива действительно равен четырем:

 ;  Как написано
   foo = myStruct-> данные [i];
 ;  Как интерпретируется (предполагая малоинтенсивное аппаратное обеспечение)
   foo = ((* (uint32_t *) myStruct-> data) >> (i << 3)) & 0xFF;

Такая оптимизация может быть разумной, особенно если myStruct->data могут быть загружены в регистр в той же операции, что и myStruct->size . Я не знаю ничего в стандарте, который запретил бы такую ​​оптимизацию, хотя, конечно, он сломал бы любой код, который мог бы ожидать доступа к материалам за пределами четвертого элемента.

Другое использование массивов с нулевой длиной – это создание объекта переменной длины (pre-C99). Массивы с нулевой длиной отличаются от гибких массивов, которые имеют [] без 0.

Цитируется из gcc doc :

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

  • Гибкие элементы массива записываются как содержимое [] без 0.
  • Элементы гибкого массива имеют неполный тип, поэтому оператор sizeof не может быть применен.

Реальным примером являются массивы с нулевой длиной struct kdbus_item в kdbus.h (модуль ядра Linux).

  • Многомерный массив с разной длиной
  • Адрес массива
  • Ошибка сегментации при больших размерах массива
  • Ошибка прерывания ловушки 6 в C
  • Создайте массив с одним и тем же элементом, который повторяется несколько раз
  • Доступ к массиву за пределами границ не дает ошибки, почему?
  • Изменение значений во время итерации в golang
  • как разрешить массив с сильными параметрами
  • Как получить доступ к многомерному массиву и управлять им с помощью имен / путей ключа?
  • Возвращаемый массив в функции
  • Как разбить строку на пробел
  • Давайте будем гением компьютера.