Почему GCC компилирует и связывает два файла, даже если extern не используется?

Ниже приведены два отдельных кода, записанных в двух отдельных файлах Test1.c и Test2.c. Я не использую ключевое слово extern ни в одном файле.

//Test1.c
#include <stdio.h>

int a = 1;
int main()
{
    printf("test1 - a val = %d\n",a);
    fn();
    printf("After Return : %d",a);
}

//Test2.c
#include <stdio.h>

int a;
int fn()
{
    printf("test2 - a val = %d\n",a);
    a++;
}

Я скомпилировал этот код с помощью gcc:

gcc Test1.c Test2.c

Он генерирует следующий вывод:

test1 - a val = 1
test2 - a val = 1

Я пробовал печатать адрес переменной a в обоих кодах. Адрес тоже такой же.

Теперь у меня следующие вопросы:

  1. gcc автоматически компилируется и связывается, даже если extern не используется ?? Здесь очевидно gcc внутренне делает это, поскольку я компилирую эти два файла вместе.
  2. Это поведение с / без ключевого слова extern зависит от компилятора?

person sandeep.ganage    schedule 12.01.2017    source источник
comment
Это не совсем соответствует стандарту C. Это «общее расширение», задокументированное в Приложении J к стандарту. См. Также раздел «Не очень хороший способ определения глобальных переменных» в Как использовать extern для обмена переменными между исходными файлами на C?   -  person Jonathan Leffler    schedule 12.01.2017
comment
Скомпилировать с опцией -fno-common.   -  person Sourav Ghosh    schedule 12.01.2017
comment
@JonathanLeffler Как это может быть дубликатом этого вопроса? Мой вопрос касался поведения extern с компилятором gcc.   -  person sandeep.ganage    schedule 12.01.2017
comment
Он спрашивает о поведении extern, а не-extern, которое рассматривается в другом вопросе. То, что вы видите, - это одна из распространенных моделей поведения. Это санкционировано стандартом как расширение, но оно не строго соответствует букве стандарта (Приложение J является «информативным», а не «нормативным»). То, что делает GCC, рассматривается в повторяющемся вопросе без вызова GCC по имени. Если вы найдете других людей, которые повторно откроют вопрос, я не буду его оспаривать, но я думаю, что это справедливое дублирующее решение.   -  person Jonathan Leffler    schedule 12.01.2017


Ответы (1)


Этот код является неопределенным поведением и не требует диагностики. Test1.c и Test2.c определяют объект a с внешней связью, что нарушает C11 6.9 / 5:

Если идентификатор, объявленный с внешней связью, используется в выражении (кроме как часть операнда оператора sizeof или _Alignof, результат которого является целочисленной константой), где-то во всей программе должен быть ровно один внешнее определение идентификатора; в противном случае их должно быть не более одного.

Примечание: «внешнее определение» означает определение в области файла. (C11 6.9 / 4, 6.9 / 5). Некоторые другие комментарии / ответы путают «внешнее определение» с «определением объекта с внешней связью» или «определение с ключевым словом extern». static int x = 5; в области файла - это внешнее определение.


Как упоминал Джонатан Леффлер в комментариях, этот конкретный результат может быть преднамеренным расширением GCC. Из Приложения J.5.11 C11 «Общие расширения»:

Для идентификатора объекта может быть несколько внешних определений с явным использованием ключевого слова extern или без него; если определения не совпадают или инициализировано более одного, поведение не определено.

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

person M.M    schedule 12.01.2017