Почему этот оператор вызывает ошибку компоновщика с gcc? - PullRequest
0 голосов
/ 29 августа 2018

У меня есть этот чрезвычайно тривиальный кусок кода C:

static int arr[];
int main(void) {
    *arr = 4;
    return 0;
}

Я понимаю, что первое утверждение недопустимо (я объявил массив области файла со статической продолжительностью хранения и связью файлов, но без указанного размера), но почему он приводит к ошибке компоновщика? :

/usr/bin/ld: /tmp/cch9lPwA.o: in function `main':
unit.c:(.text+0xd): undefined reference to `arr'
collect2: error: ld returned 1 exit status

Разве компилятор не сможет перехватить это перед компоновщиком?

Мне также странно, что, если я опускаю класс хранения static, компилятор просто предполагает, что массив имеет длину 1 и не выдает никаких ошибок, кроме этого:

int arr[];
int main(void) {
    *arr = 4;
    return 0;
}

Результаты:

unit.c:5:5: warning: array 'arr' assumed to have one element
 int arr[];

Почему пропуск класса хранилища приводит к другому поведению и почему первый фрагмент кода вызывает ошибку компоновщика? Спасибо.

Ответы [ 2 ]

0 голосов
/ 04 сентября 2018

Возможно, одной из причин такого поведения является то, что компилятор выдает предупреждение, приводящее к недоступной переменной static, и оптимизирует ее - компоновщик будет жаловаться!

Если он не является статическим, его просто нельзя игнорировать, поскольку другие модули могут ссылаться на него - так что компоновщик может по крайней мере найти этот символ arr.

0 голосов
/ 04 сентября 2018

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

...