Extern в нескольких файлах и возможное двойное определение
Я запускал следующие коды, скомпилированные вместе как: gcc A.c B.c -o combined
Программа А:
#include<stdio.h>
int a=1;
int b;
int main()
{
extern int a,b;
fun();
printf("%d %d\n",a,b);
}
Программа Б:
int a;
int b=2;
int fun()
{
printf("%d %d\n",a,b);
return 0;
}
При запуске "комбинированной" программы результат был:
1 2
1 2
Теперь у меня есть несколько сомнений по поводу этого:
Почему не вывод:
0 2
1 0
Разве a и b не определены дважды?
Пожалуйста, объясните это ясно, у меня было много проблем с пониманием внешности, и лишь немногие из этих сомнений продолжают появляться время от времени.
Заранее спасибо.
4 ответа
Итак, я отвечаю на свой вопрос через долгое время. Хотя утверждение:
int b;
это декаларация иint b = 2;
это определение
верно, но причина, по которой все говорят, не ясна.
Если бы не было int b = 2;
, int b;
было определение, так в чем же разница?
Разница заключается в том, как компоновщик обрабатывает несколько определений символов. Есть понятие слабых и сильных символов.
Ассемблер неявно кодирует эту информацию в таблице символов перемещаемого объектного файла. Функции и инициализированные глобальные переменные получают сильные символы. Неинициализированные глобальные переменные получают слабые символы.
Так в Program A
, int a = 1
сильный символ в то время как int b;
является слабым символом, аналогично в Program B
, int b = 2
сильный символ и в то время как int a
слабый.
Учитывая это понятие сильных и слабых символов, линкеры Unix используют следующие правила для работы с множественно определенными символами:
- Несколько сильных символов не допускаются.
- Учитывая сильный символ и несколько слабых символов, выберите сильный символ.
- Учитывая несколько слабых символов, выберите любой из слабых символов.
Итак, теперь мы можем спорить о том, что происходит в вышеуказанном случае.
- среди
int b = 2
а такжеint b
первый является сильным символом, а второй слабым, так что b определяется значением 2. - среди
int a = 1
а такжеint a
, а определяется как 1 (то же самое рассуждение).
Следовательно, выход 1 2
,
Переменная может быть объявлена много раз, если декларации согласуются друг с другом и с определением. Он может быть объявлен во многих модулях, включая модуль, в котором он был определен, и даже много раз в одном и том же модуле.
Внешняя переменная также может быть объявлена внутри функции. В этом случае необходимо использовать ключевое слово extern, в противном случае компилятор будет считать его определением локальной переменной, которая имеет другую область видимости, время жизни и начальное значение. Это объявление будет видно только внутри функции, а не внутри модуля функции.
Теперь позвольте мне повторить определение extern, которое гласит: "внешняя переменная - это переменная DEFINED вне любого функционального блока"(пожалуйста, внимательно прочитайте слово, выделенное жирным шрифтом). Так что для Programe A
a
есть определение, но b
это просто объявление, поэтому extern будет искать его определение 'b', которое дано в Programe B
. Так распечатать из Programe A
является 1 2
. Теперь давайте поговорим о Programe B
которые имеют декларацию для a
и определение для b
так что ценится ценность a
от programe A
и значение b
из текущего файла.
Потому что переменные здесь не определены дважды; они объявлены дважды, хотя. Функции берут значения из определения переменных, а не из объявления переменных.
Объявление вводит идентификатор и описывает его тип. Через объявление мы заверяем компилятору, что эта переменная или функция была определена где-то еще в программе и будет предоставлена во время компоновки. Как, например, объявление:
extern int a;
Определение фактически создает / реализует этот идентификатор. Определение таково:int a=5;
ИЛИ ЖЕ int a;
Просто прочитайте эту ссылку для дальнейшего использования.
есть и этот замечательный пост на stackru.
extern
сообщает компилятору, что переменная определена снаружи, поэтому она смотрит за пределы функции и находит:
int a=1
в программе А и int b=2
в программе B
Для переменных AUTO:
int a;//both definition and declaration
Для получения дополнительной информации о классах хранения вы можете перейти по этой ссылке
int a
вне главной или любой другой функции - это объявление (т. е. GLOBAL) только внутри любой функции, которая называется определением.
Насколько я знаю: вывод будет 1 2 и 1 2, потому что вы определяете a и b как внешние переменные в основной функции. Поэтому он будет пытаться получить значение и из других файлов. Что касается 2-го вопроса, я думаю, что компилятор берет инициализированные значения переменной и объединяет их, потому что и a, и b определены как глобальные переменные в обоих файлах. Случай может быть другим, если оба были определены внутри функции. Любые предложения или другие материалы приветствуются.