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

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

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

#include <stdio.h>

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

Разъяснение

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

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

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

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

Ответы [ 7 ]

79 голосов
/ 15 марта 2012

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

ISO 9899: 2011 6.7.6.2:

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

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

gcc -std=c99 -pedantic выдает предупреждение для случая без VLA.

74 голосов
/ 15 марта 2012

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

Тем не менее, в настоящее время в Си практикуется использование гибких массивов .

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) или struct hack (спасибо Джо Вершнигу за указание на это).

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

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

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

57 голосов
/ 15 марта 2012

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

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

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

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

24 голосов
/ 15 марта 2012

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

char someCondition[ condition ];

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

char someCondition[ 2 * condition - 1 ];

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

8 голосов
/ 15 марта 2012

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

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

В GNU C допускаются массивы нулевой длины.

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

и

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

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

int arr[0] = { 1 };

и бум: -)

7 голосов
/ 30 апреля 2015

Другое использование массивов нулевой длины - для создания объекта переменной длины (до 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 голосов
/ 15 марта 2012

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

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

typedef struct {
  uint32_t size;
  uint8_t data[4];  // Use four, to avoid having padding throw off the size of the struct
}

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

; As written
  foo = myStruct->data[i];
; As interpreted (assuming little-endian hardware)
  foo = ((*(uint32_t*)myStruct->data) >> (i << 3)) & 0xFF;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...