ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.12.2020
Просмотров: 4253
Скачиваний: 28
Глава 7. Структура программы на языке С
91
блок, в котором объявлены локальные переменные, память под
эти переменные распределяется, а при возврате из функции или
выходе из блока эта память освобождается. Поэтому данные, со-
держащиеся в локальных переменных, недоступны при после-
дующих входах в блок или вызовах функции. Если локальная пе-
ременная не проинициализирована при ее объявлении, то ее на-
чальное значение не определено.
Функции существуют в течение всего времени исполнения про-
граммы.
7.3. Связывание идентификаторов
Программа на языке программирования С состоит из одного или
нескольких исходных файлов, которые содержат код программы.
Эти файлы также называются
единицами компиляции
, т. к. каж-
дый из них является исходным данным для компилятора. Резуль-
татом компиляции исходного файла является объектный файл,
который представляет собой программу в кодах микропроцессо-
ра, эквивалентную по смыслу исходной программе на языке про-
граммирования С. Если программа на языке программирования С
состоит из нескольких исходных файлов, то в результате ее ком-
пиляции получается такое же количество объектных файлов. Все
эти объектные файлы нужно собрать в один исполняемый мо-
дуль. Эту задачу решает редактор связей (linker), который также
называется компоновщиком.
При разработке программы, состоящей из нескольких исходных
файлов, может возникнуть следующая проблема. В одном исход-
ном файле нужно сослаться на идентификатор переменной или
функции, которая определена в другом исходном файле. Для ре-
шения этой проблемы между одинаковыми идентификаторами в
разных исходных файлах программы устанавливается связь, ко-
торая называется
связыванием
или
сцеплением
(linkage)
перемен-
ных
. Связывание реализуется при помощи назначения идентифи-
катору типа связывания, который определяет видимость иденти-
фикатора в других исходных файлах. Определены три типа
связывания идентификаторов:
Часть I. Язык программирования С
92
внутреннее связывание, т. е. на идентификатор можно ссы-
латься только внутри исходного файла, в котором он опреде-
лен;
внешнее связывание, т. е. на идентификатор можно ссылаться
как внутри исходного файла, в котором он определен, так и в
других исходных файлах;
отсутствие связывания, т. е. на идентификатор можно ссы-
латься только внутри блока, в котором он определен.
Глобальные переменные и функции имеют внешнее связывание,
т. е. на них можно ссылаться в других исходных файлах. Локаль-
ные переменные не имеют связывания.
Так как глобальная переменная имеет внешнее связывание, то
глобальная переменная с одним и тем же именем может быть
объявлена в программе только один раз. Если глобальные пере-
менные с одним именем будут объявлены в разных исходных
файлах программы, то компилятор эти объявления пропустит, а
компоновщик выдаст ошибку, указывающую на повторение име-
ни переменной.
В связи с введением понятия связывания идентификаторов нужно
различать объявление и определение переменной. Если объявле-
ние переменной вызывает распределение памяти для этой пере-
менной, то такое объявление называется
определением переменной
.
7.4. Спецификаторы классов памяти
В языке программирования C для управления временем сущест-
вования переменных и связыванием идентификаторов использу-
ются специальные ключевые слова, которые называются
специ-
фикаторами классов памяти
. Отметим, что спецификаторы
классов памяти не изменяют области видимости идентификатора.
Существуют четыре спецификатора классов памяти:
extern
,
auto
,
static
и
register
. Используются спецификаторы классов памяти
при объявлении переменных и функций и записываются перед
идентификатором или спецификатором типа переменной или
функции.
Глава 7. Структура программы на языке С
93
Спецификатор
extern
Спецификатор
extern
управляет связыванием идентификаторов и
указывает компилятору, что переменная или функция уже опре-
делена вне области видимости своего объявления в том же или
другом исходном файле.
Если спецификатор
extern
используется при объявлении локаль-
ной переменной, то в этом случае он указывает компилятору, что
эта переменная ссылается на глобальную переменную с тем же
именем, определенную в том же или другом исходном файле.
Этот случай рассмотрен в листинге 7.2.
Листинг 7.2. Использование спецификатора
extern
с локальной переменной
#include <stdio.h>
int n = 1;
int main(void)
{
int n = 2;
{
extern int n;
printf("n = %d\n", n); /* печатает n = 1 */
}
return 0;
}
Если спецификатор
extern
используется с глобальной перемен-
ной, то это указывает компилятору, что эта переменная ссылается
на глобальную переменную с тем же именем, но которая опреде-
лена в другом исходном файле. Этот случай рассмотрен в лис-
тингах 7.3 и 7.4, которые описывают исходные файлы, принадле-
жащие одной программе.
Листинг 7.3. Исходный файл с определением
глобальной переменной и прототипом функции
#include <stdio.h>
int n;
Часть I. Язык программирования С
94
void foo(void);
int main(void)
{
foo();
printf("n = %d\n", n); /* печатает n = 1 */
return 0;
}
Листинг 7.4. Исходный файл с объявлением
глобальной переменной и определением функции
extern int n;
void foo(void) { ++n; }
Спецификатор
extern
также может использоваться при объявле-
нии функции. Это указывает компилятору, что функция опреде-
лена в другом исходном файле. Однако объявление функции
имеет этот спецификатор по умолчанию и поэтому он редко ис-
пользуется в этом случае. В листинге 7.4 определена функция
foo
, которая вызывается в листинге 7.3. Поэтому в листинге 7.3
эта функция только объявляется перед своим использованием.
Спецификатор
auto
Спецификатор
auto
управляет временем существования локаль-
ной переменной и указывает компилятору, что переменная, опре-
деленная внутри блока, существует только во время исполнения
этого блока. Локальные переменные имеют этот спецификатор по
умолчанию, поэтому он редко используется при объявлении та-
ких переменных.
Спецификатор
static
Спецификатор
static
управляет временем существования ло-
кальных переменных и связыванием идентификаторов глобаль-
ных переменных и функций.
Если локальная переменная определена со спецификатором
static
, то она существует в течение всего времени исполнения
программы. Если такая переменная не инициализируется при
Глава 7. Структура программы на языке С
95
своем определении, то компилятор устанавливает начальное зна-
чение этой переменной в ноль.
В листинге 7.5 приведена программа, в которой показано как из-
меняется значение статической локальной переменной.
Листинг 7.5. Изменение значения статической локальной
переменной
#include <stdio.h>
int count(void)
{
static int c;
return ++c;
}
int main(void)
{
count();
printf("count = %d\n", count()); /* печатает count = 2 */
return 0;
}
Для глобальной переменной или функции спецификатор
static
ограничивает ее связывание только исходным файлом, в котором
эта переменная или функция определена. То есть к этой перемен-
ной или функции нельзя обратиться из другого исходного файла.
Например, если в листингах 7.3 и 7.4 добавить спецификатор
static
в определения глобальной переменной
n
и функции
foo
, то
на них нельзя будет сослаться в других исходных файлах. В этом
случае программы успешно пройдут компиляцию, однако редак-
тирование связей вызовет ошибку.
Спецификатор
register
Спецификатор
register
указывает компилятору, что значение
переменной нужно хранить в регистре микропроцессора. Этот
спецификатор может применяться только к локальным перемен-
ным и параметрам функций. Компилятор не обязан исполнять
требования спецификатора
register
и может хранить значения