Поделиться через


Спецификаторы классов хранения для объявлений внешнего уровня

Внешние переменные — это переменные в области видимости файла. Они определяются вне конкретной функции и потенциально доступны для множества функций. Функции можно определить только на внешнем уровне, и, следовательно, они не могут быть вложенными. По умолчанию все ссылки на внешние переменные и функции с одинаковым именем являются ссылками на один и тот же объект, что означает, что они имеют внешнюю компоновку. (Чтобы переопределить такое поведение можно использовать ключевое слово static. Дополнительные сведения о ключевом слове static см. далее в этом разделе.)

Объявления переменных на внешнем уровне являются определениями переменных (определяющие объявления) или ссылками на переменные, определенные в другом месте (ссылающиеся объявления).

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

  • Переменная, объявляемая с описателем класса хранения static. Можно явно инициализировать переменную static с константным выражением, как описано в разделе Инициализация. Если опустить инициализатор, переменная инициализируется значением 0 по умолчанию. Например, следующие два оператора считаются определениями переменной k.

    static int k = 16;
    static int k;
    
  • Переменная, которая явно инициализируется на внешнем уровне. Например, int j = 3; — это определение переменной j.

В объявлениях переменных на внешнем уровне (т. е. вне всех функций) можно использовать описатель класса хранения static или extern либо полностью опустить описатель класса хранения. Использовать терминалы storage-class-specifier auto и register на внешнем уровне невозможно.

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

Ниже приводятся правила в отношении static.

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

  • Указать переменную на внешнем уровне можно только один раз в рамках программы. Можно указать другую переменную с тем же именем и описатель класса хранения static в другой записи преобразования. Поскольку каждое определение static является видимым только в пределах собственной записи преобразования, конфликт не произойдет. Это удобный способ скрыть имена идентификаторов, которые должны совместно использоваться функциями в одной записи преобразования, но должны быть невидимы для других записей преобразования.

  • Описатель класса хранения static также может применяться к функциям. При объявлении функции static, ее имя невидимо вне файла, в котором она объявлена.

Ниже приведены правила использования extern.

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

  • Чтобы ссылка extern стала допустимой, необходимо объявить переменную, на которую она ссылается, один и только один раз на внешнем уровне. Это определение (без класса хранения extern) может находиться в любой из записей преобразования, составляющих программу.

Пример

В примере ниже показаны внешние объявления.

/******************************************************************
                      SOURCE FILE ONE 
*******************************************************************/
#include <stdio.h>

extern int i;                // Reference to i, defined below 
void next( void );           // Function prototype            

int main()
{
    i++;
    printf_s( "%d\n", i );   // i equals 4 
    next();
}

int i = 3;                  // Definition of i

void next( void )
{
    i++;
    printf_s( "%d\n", i );  // i equals 5
    other();
}

/******************************************************************
                      SOURCE FILE TWO 
*******************************************************************/
#include <stdio.h>

extern int i;              // Reference to i in 
                           // first source file 
void other( void )
{
    i++;
    printf_s( "%d\n", i ); // i equals 6 
}

Два исходных файла в этом примере содержат три внешних объявления i. Только одно объявление является определяющим. Данное объявление

int i = 3;

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

extern int x;

и что определяющая ссылка

int x = 0;

отображается в другой записи преобразования программы.

Все три функции (main, next и other) выполняют одну и ту же задачу: они расширяют переменную i и отображают ее значение. Отображаются значения 4, 5 и 6.

Если переменная i не была бы инициализирована, ей автоматически было бы присвоено значение 0. В этом случае были бы отображены значения 1, 2 и 3. Дополнительные сведения об инициализации переменных см. в разделе Инициализация.

См. также

Ссылки

Классы хранения в C