Спецификатор extern

Прежде чем приступить к рассмотрению спецификатора extern, необходимо коротко остановиться на компоновке программы. В языке С при редактировании связей к переменной может применяться одно из трех связываний: внутреннее, внешнее или же не относящееся ни к одному из этих типов. (В последнем случае редактирование связей к ней не применяется.) В общем случае к именам функций и глобальных переменных применяется внешнее связывание. Это означает, что после компоновки они будут доступны во всех файлах, составляющих программу. К объектам, объявленным со спецификатором static и видимым на уровне файла, применяется внутреннее связывание, после компоновки они будут доступны только внутри файла, в котором они объявлены. К локальным переменным связывание не применяется и поэтому они доступны только внутри своих блоков.

Спецификатор extern указывает на то, что к объекту применяется внешнее связывание, именно поэтому они будут доступны во всей программе. Далее нам понадобятся чрезвычайно важные понятия объявления и описания. Объявление(декларация) объявляет имя и тип объекта. Описание[1] выделяет для объекта участок памяти, где он будет находиться. Один и тот же объект может быть объявлен неоднократно в разных местах, но описан он может быть только один раз.

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

Приведем пример использования спецификатора extern. Обратите внимание, что глобальные переменные first и lastобъявлены после main().

#include <stdio.h>

int main(void)

{

extern int first, last; /* используются глобальные переменные */

printf("%d %d", first, last);

return 0;

}

/* описание глобальных переменных first и last */

int first = 10, last = 20;

 

Программа напечатает 10 20, потому что глобальные переменные first и last инициализированы этими значениями. Объявление extern сообщает компилятору, что переменные first и last определены в другом месте, поэтому программа компилируется без ошибки, несмотря даже на то, что first и last используются до своего описания.

Обратите внимание, в этом примере объявление переменных со спецификатором extern необходимо только потому, что они не были объявлены до main(). Если бы их объявление встретилось перед main(), то в объявлении со спецификатором externне было бы необходимости.

При компиляции выполняются следующие правила. Если компилятор находит переменную, не объявленную внутри блока, он ищет ее объявление во внешних блоках. Если не находит ее и там, то ищет среди объявлений глобальных переменных. В предыдущем примере, если бы не было объявления extern, компилятор не нашел бы first и last среди глобальных переменных, потому что они объявлены после main(). Здесь спецификатор extern сообщает компилятору, что эти переменные будут объявлены в файле позже.

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

Спецификатор extern играет большую роль в программах, состоящих из многих файлов. В языке С программа может быть записана в нескольких файлах, которые компилируются раздельно, а затем компонуются в одно целое. В этом случае необходимо как-то сообщить всем файлам о глобальных переменных программы. Самый лучший (и наиболее переносимый) способ сделать это — определить (описать) все глобальные переменные в одном файле и объявить их со спецификатором extern в остальных файлах, как показано на рис. 2.1.

Файл 1 Файл 2 int x, y; extern int x, y; char ch; extern char ch; int main(void) void func22(void) { { /* ... */ x = y / 10; } } void func1(void) void func23(void) { { x = 123; y = 10; } }
Рис. 2.1. Использование глобальных переменных в раздельно компилируемых модулях

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

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

На заметку Спецификатор extern можно применять в объявлении функций, но в этом нет необходимости.