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