Пустые массивы static int arr[];
и массивы нулевой длины static int arr[0];
были gcc нестандартными расширениями .
Намерение этих расширений заключалось в том, чтобы действовать как исправление для старого "struct hack" Еще в дни C90 люди писали такой код:
typedef struct
{
header stuff;
...
int data[1]; // the "struct hack"
} protocol;
где data
будет использоваться так, как если бы он имел переменный размер вне массива в зависимости от того, что находится в части заголовка Такой код содержал ошибки, записывал данные в байты заполнения и вообще вызывал неопределенное поведение массива.
gcc исправил эту проблему, добавив пустые / нулевые массивы в качестве расширения компилятора, благодаря чему код работал без ошибок, хотя он больше не был переносимым.
Комитет по стандартизации C признал, что эта функция gcc была полезна, поэтому он добавил члены гибкого массива к языку C в 1999 году. С тех пор функция gcc должна считаться устаревшей, так как использование C стандартный гибкий элемент массива является предпочтительным.
Как признано в связанной документации gcc:
Не рекомендуется объявлять массивы нулевой длины в других контекстах, в том числе в качестве внутренних элементов объектов структуры или объектов, не являющихся членами.
И это то, что делает ваш код.
Обратите внимание, что gcc без параметров компилятора по умолчанию принимает значение -std=gnu90
(gcc <5.0) или <code>-std=gnu11 (gcc> 5.0). Это дает вам все включенные нестандартные расширения, поэтому программа компилируется, но не связывается.
Если вы хотите стандартное совместимое поведение, вы должны скомпилировать как
gcc -std=c11 -pedantic-errors
Флаг -pedantic
отключает расширения gcc, и ошибка компоновщика переключается на ошибку компилятора, как и ожидалось. Для пустого массива, как в вашем случае, вы получите:
ошибка: в массиве отсутствует размер массива
А для массива нулевой длины вы получите:
ошибка: ISO C запрещает массив нулевого размера 'arr' [-Wpedantic]
Причина, по которой работает int arr[]
, заключается в том, что это объявление массива предварительное определение с внешней связью (см. C17 6.9.2). Это действительный C и может рассматриваться как предварительное заявление. Это означает, что в другом месте кода компилятор (или, скорее, компоновщик) должен ожидать найти, например, int arr[10]
, который затем ссылается на ту же самую переменную. Таким образом, arr
можно использовать в коде до того, как размер станет известен. (Я бы не рекомендовал использовать эту языковую функцию, так как это форма «программирования спагетти».)
Когда вы используете static
, вы блокируете возможность указать размер массива в другом месте, заставляя переменную вместо этого иметь внутреннюю связь.