Использование extern в том же файле - PullRequest
0 голосов
/ 05 июня 2018

Я запутался с использованием extern в том же файле, как показано в коде ниже.Первый случай был фактически решением для печати глобальной переменной в C (когда существуют локальные переменные с таким же именем), но я не могу понять, как это работает и как не работает третий случай.

Случай1:

int a = 10;
int main()
{
    int a = 20;
    {
        extern int a; // Is this telling the linker to use global variable? 
        printf("A value: %d\n", a);
    }
    return 0;
}

Дело 2:

extern int a; // If in the above case extern was telling linker to use global variable 
              // then how in this local variable is getting referred
int main()
{
    int a = 20;
    {
        printf("A value: %d\n", a);
    }
    return 0;
}

Дело 3:

// int a = 10;
int main()
{
    int a = 20;
    {
         extern int a; // Why now I get a linking error
         printf("A value: %d\n", a);
    }
    return 0;
}

Ответы [ 4 ]

0 голосов
/ 05 июня 2018

(Обратите внимание, что изменения в коде в вопросе, кажется, делают части этого ответа более не совсем правильными.)

Per 6.2.2 Связи идентификаторов , пункт 4 стандарт C :

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

Итак, в ваших первых двух случаяхвнутренний extern int a; имеет предыдущее объявление - глобальное int a; в вашем первом случае или extern int a; во втором - поэтому объявление extern int a; относится к глобальному.

Для третьего случая:остальная часть параграфа 4 актуальна:

Если предыдущее объявление не видно или если в предыдущем объявлении не указана связь, то идентификатор имеет внешнюю связь.

Также в параграфе 6 говорится:

Следующие идентификаторы не имеют связи: идентификатор, объявленный как что-либо, кроме объекта или функции;идентификатор, объявленный как параметр функции; идентификатор области блока для объекта, объявленного без спецификатора класса хранения extern.

Таким образом, объявление в вашем третьем случае ссылается на extern int a;

Однако нигде не определено фактическое int a;.И поскольку extern int a; в вашем третьем примере появляется в области видимости блока, а фактическое определение объекта int a; в другом месте отсутствует, ваша программа не может установить связь.

Per 6.9.2 Внешнийопределения объекта , абзац 2 гласит:

Объявление идентификатора для объекта, который имеет область действия файла без инициализатора и без спецификатора класса хранения или сспецификатор класса хранения static представляет собой предварительное определение.Если модуль перевода содержит одно или несколько предварительных определений для идентификатора, а модуль перевода не содержит внешнего определения для этого идентификатора, то поведение точно такое, как если бы модуль перевода содержал объявление области файла для этого идентификатора с составным типом какконца единицы перевода, с инициализатором, равным 0.

Таким образом, объявление block-scope extern int a; вашего третьего случая не квалифицируется как предварительное определение.

0 голосов
/ 05 июня 2018

В первом случае у вас есть глобальный a, который вы переопределяете локальным (автоматическим) a, который вы снова переопределяете с глобальным a (extern может ссылаться только на глобальные переменные в некотором модуле).Он напечатает 10.

Во втором случае у вас есть глобальный a, который находится в этом или другом модуле (файл / модуль компиляции), который вы переопределяете локальным a.Он напечатает 20.

В третьем случае у вас есть локальный a, который вы переопределяете глобальным a, который, очевидно, не существует ни в одном из ваших блоков компиляции, следовательно, ошибка компоновщика.

0 голосов
/ 05 июня 2018

Случай 1: - В первом блоке кода extern int a; указывает, что здесь объявлена ​​переменная a, но указывает компоновщику найти определение a выше main(), а не в main(),Если компоновщик может найти определение a выше main(), тогда он будет ссылаться, иначе приведет к ошибке компоновщика.В вашем случае компоновщик примет a как 10.

Случай 2: - В этом случае глобально объявленный extern int a; говорит компоновщику, что определение a можетнаходиться в другом файле или в том же файле в функции main().Здесь extern int a; говорит, что если вам это нужно, будет определено значение * со статической продолжительностью и внешней связью (a глобальная переменная), определенное либо в этом файле , либо в другом файле .Это объявление скрыто определением внутри main(), поэтому printf() использует local variable.Это

printf("A value: %d\n",a);

считается локально объявленным a.Таким образом, он печатает 20.

Случай 3: - В этом случае оператор

extern int a; // Why now I get a linking error

вызывает ошибку компоновщика, поскольку компоновщик попытается найти определение a выше main() и его там нет (вы прокомментировали), поэтому это приводит к ошибке компоновщика .

0 голосов
/ 05 июня 2018

Случай 1:

Объявление extern int a ; объявляет переменную int a, которая определена в другом месте, и, таким образом, скрывает переменную a, объявленную здесь: int a = 20 ;.Затем компоновщик ищет глобальную переменную a и находит ее в том же файле из-за объявления int a = 10 ;.Выходные данные 10.

Случай 2:

Здесь объявление extern int a ; не действует.Внутри main используется локальная переменная, объявленная здесь int a = 20 ;, и, следовательно, вывод равен 20.

Случай 3:

Это похоже на случай 1Он правильно компилируется, но не связывается, потому что extern int a ; объявляет переменную int a, которая предположительно определена в другом месте, а это не так, потому что вы закомментировали объявление int a = 10 ;.

...