В языке программирования C и его предшественнике B внешняя переменная — это переменная, определенная вне любого функционального блока. С другой стороны, локальная (автоматическая) переменная — это переменная, определенная внутри функционального блока.
В качестве альтернативы автоматическим переменным можно определить переменные, которые являются внешними по отношению ко всем функциям, то есть переменные, к которым может получить доступ по имени любая функция. (Этот механизм скорее похож на переменные Fortran COMMON или Pascal , объявленные в самом внешнем блоке.) Поскольку внешние переменные доступны глобально, их можно использовать вместо списков аргументов для передачи данных между функциями. Кроме того, поскольку внешние переменные существуют постоянно, а не появляются и исчезают при вызове и выходе из функций, они сохраняют свои значения даже после того, как функции, которые их установили, вернулись.
В языке B все переменные должны быть объявлены как auto, extrn или неявно как аргументы функции. [1] Внешняя переменная определяется вне функций, имеет время жизни, равное всему выполнению программы, и вводится в функцию посредством объявления extrn
.
В качестве примера можно использовать следующий код из учебника: [1]
main ( ) { extrn a , b , c ; putchar ( a ); putchar ( b ); putchar ( c ); putchar ( ' !* n ' ); } а ' ад ' ; б ' о , в ' ; в ' мир ' ;
a
, b
, c
— все это внешние переменные программы, определенные вне функций и введенные в main
функцию посредством extrn
объявлений.
Чтобы понять, как внешние переменные связаны с extern
ключевым словом, необходимо знать разницу между определением и объявлением переменной. Когда переменная определена , компилятор выделяет память для этой переменной и, возможно, также инициализирует ее содержимое некоторым значением. Когда переменная объявлена , компилятор требует, чтобы переменная была определена в другом месте. Объявление сообщает компилятору, что переменная с таким именем и типом существует, но компилятору не нужно выделять для нее память, так как она выделена в другом месте. Ключевое extern
слово означает «объявить без определения». Другими словами, это способ явно объявить переменную или принудительно объявить ее без определения. Также возможно явно определить переменную, т. е. принудительно определить ее. Это делается путем присвоения переменной значения инициализации. Если ни extern
ключевое слово, ни значение инициализации отсутствуют, оператор может быть либо объявлением, либо определением. Компилятор должен проанализировать модули программы и принять решение.
Переменная должна быть определена ровно один раз в одном из модулей программы. Если определения нет или их больше одного, возникает ошибка, возможно, на этапе связывания. Переменная может быть объявлена много раз, пока объявления согласуются друг с другом и с определением (что в значительной степени облегчают заголовочные файлы ). Она может быть объявлена во многих модулях, включая модуль, в котором она была определена, и даже много раз в одном и том же модуле. Но обычно бессмысленно объявлять ее более одного раза в модуле.
Внешняя переменная также может быть объявлена внутри функции. В этом случае extern
необходимо использовать ключевое слово, иначе компилятор посчитает это определением локальной ( автоматической ) переменной, которая имеет другую область действия, время жизни и начальное значение. Это объявление будет видно только внутри функции, а не во всем модуле функции.
Ключевое extern
слово, примененное к прототипу функции, не делает абсолютно ничего ( extern
ключевое слово, примененное к определению функции, конечно, бессмысленно). Прототип функции всегда является объявлением и никогда не определением. Кроме того, в стандартном C функция всегда является внешней, но некоторые расширения компилятора позволяют определять функцию внутри функции.
Внешняя переменная должна быть определена ровно один раз вне любой функции; это выделяет для нее память. Переменная также должна быть объявлена в каждой функции, которая хочет получить к ней доступ; это указывает тип переменной. Объявление может быть явным
extern
утверждением или может подразумеваться из контекста. ... Обратите внимание, что мы используем слова определение и объявление осторожно, когда ссылаемся на внешние переменные в этом разделе. Определение относится к месту, где создается переменная или назначается память; объявление относится к местам, где указывается природа переменной, но память не выделяется.
К внешней переменной могут обращаться все функции во всех модулях программы. Это глобальная переменная . Чтобы функция могла использовать переменную, объявление или определение внешней переменной должно находиться перед определением функции в исходном коде. Или должно быть объявление переменной с ключевым словом extern
внутри функции.
Ключевое static
слово ( static
и extern
являются взаимоисключающими), примененное к определению внешней переменной, немного меняет это: доступ к переменной могут получить только функции в том же модуле, где она была определена. Но функция в том же модуле может передать ссылку (указатель) на переменную другой функции в другом модуле. В этом случае, даже если функция находится в другом модуле, она может читать и изменять содержимое переменной — она просто не может ссылаться на нее по имени.
Также возможно использовать static
ключевое слово при определении локальной переменной. Без static
ключевого слова переменная автоматически выделяется при вызове функции и освобождается при выходе из функции (отсюда и название «автоматическая переменная»). Ее значение не сохраняется между вызовами функций. С ключевым словом static
переменная выделяется при запуске программы и освобождается при ее завершении. Ее значение не теряется между вызовами функций. Переменная по-прежнему является локальной, поскольку к ней можно получить доступ только по имени внутри функции, которая ее определила. Но ссылку (указатель) на нее можно передать другой функции, что позволит ей читать и изменять содержимое переменной (опять же, не ссылаясь на нее по имени).
Внешние переменные выделяются и инициализируются при запуске программы, а память освобождается только при завершении программы. Их время жизни такое же, как и у программы.
Если инициализация не выполнена явно, внешние (статические или нет) и локальные статические переменные инициализируются нулем. Локальные автоматические переменные неинициализированы, т.е. содержат «мусорные» значения.
Ключевое static
слово, примененное к определению функции, изменяет связь функции так, что она становится видимой только из единицы трансляции, где находится ее определение. Это предотвращает вызов функции по имени извне ее модуля (остается возможность передать указатель функции из модуля и использовать его для вызова функции). Объявление функции с использованием static
ключевого слова также является хорошим способом сохранить ее имя коротким, избегая при этом конфликтов имен.
Файл 1:
// Явное определение, это фактически выделяет // а также описывает int global_variable ; // Прототип функции (декларация), предполагает, // что она определена в другом месте, обычно из включаемого файла. void some_function ( void ); int main ( void ) { global_variable = 1 ; some_function (); return 0 ; }
Файл 2:
// Неявное объявление, это только описывает и // предполагает, что память выделена в другом месте, обычно из include extern int global_variable ; // Заголовок функции (определение) void some_function ( void ) { ++ global_variable ; }
В этом примере переменная Global_Variable определена в Файле 1. Чтобы использовать ту же переменную в Файле 2, ее необходимо объявить . Независимо от количества файлов глобальная переменная определяется только один раз; однако она должна быть объявлена в любом файле за пределами того, который содержит определение.
Если программа находится в нескольких исходных файлах, а переменная определена в file1 и используется в file2 и file3, то extern-объявления необходимы в file2 и file3 для связи вхождений переменной. Обычной практикой является сбор extern-объявлений переменных и функций в отдельном файле, исторически называемом заголовком, который включается с помощью #include в начале каждого исходного файла. Суффикс .h является общепринятым для имен заголовков.
Обычная методология заключается в том, чтобы распределение и фактические определения помещались в файлы .c, но простые объявления и прототипы не распределяют, а просто описывают типы и параметры, чтобы компилятор мог работать правильно, и эта информация должна быть помещена в заголовочный файл .h, который другие могут безопасно включать без каких-либо возможных конфликтов.