Блочная структура программы
Как было отмечено выше, тело функции может быть блоком или составным оператором. Блок как совокупность описаний объектов и операторов может включать вложенные блоки, и тогда появляются внешние и локальные переменные по отношению к блоку, а также глобальные переменные, описанные вне функций программы.
В дополнение к атрибутам имени и типа объекта существует ещё два атрибута – область действия и время жизни, определяемые классом хранения (памяти) по умолчанию или задаваемые программистом. Область действия (видимости) – это часть текста программы, в котором видим и может быть использован данный объект. Переменные, описываемые в охватывающем блоке, являются внешними для вложенных блоков и включают их в область своего действия за одним исключением – внутренняя переменная имеет приоритет над одноимённой внешней переменной, закрывая её, и даже тип внутренней переменной может быть переопределён.
Время жизни – это интервал времени, в течение которого значение объекта (переменной или функции) доступно для использования в некоторой части программы. Время жизни переменной может быть локальным или глобальным. Объект, с глобальным временем жизни, имеет распределённую для него память и определённое значение на протяжении всего времени выполнения программы. Для локального объекта выделяется новая область памяти при каждом входе в блок и освобождается при выходе из блока, при этом значение объекта теряется. Переменные, описанные в функции, в том числе формальные параметры имеют локальную область действия. Сами функции в программе являются внешними по отношению друг к другу и имеют глобальное время жизни, то есть существуют на протяжении всего времени выполнения программы.
Си-программа является отдельным программным модулем, который оформляется и записывается во внешнюю память как исходный файл с расширением “.c”, например, myprog.c.
Структуру модуля программы и области действия её объектов можно представить следующей схемой:
/* Глобальные объекты и описания */ /* Область действия */
директивы предпроцессора глобальная макроопределения
прототипы функций
описания типов
описания глобальных переменных
заголовок функции локальная
{внешние переменные блока;
операторы;
{ внутренний блок локальная
}
}
описания других функций
/* конец программы */
Пример. Применение вложенных блоков с одноимёнными переменными.
Программа:
void main ( )
{ int i=2; /* i – переменная внешнего блока */
int count=0; /* count – внешняя переменная */
while ( count <= i ) /* цикл внешнего блока */
{ int=0; /* i – локальная переменная внутреннего блока */
count++; /* счётчик циклов */
printf ( “В цикле: count=%d; i=%d\n”, count, i );
}
/* конец внутреннего блока, возврат к переменной i внешнего блока */
printf ( “Вне цикла: count=%d, i=%d”, count, i );
}
Результат программы:
В цикле count=1 i=0
В цикле count=2 i=0
В цикле count=3 i=0
Вне цикла count=3 i=2
Программист может явно задать атрибуты области действия и времени жизни с помощью спецификаторов классов памяти (хранения): для переменных (auto – локальный, register – регистровый, static – статический, extern – внешний) и для функций (static, extern).
Переменные с классом хранения auto (принимается по умолчанию) и register относятся к локальным в блоке и по области действия и по времени жизни. Для переменных с классом auto выделяется память в стеке (временная память), а с классом register – в одном из свободных регистров процессора.
Память для переменных с классом static отводится в сегменте данных (статическая память программы), а не в стеке, благодаря чему они сохраняют своё значение при выходе из блока. Если отсутствует явная инициализация таких переменных, то по умолчанию они устанавливаются в 0. Инициализация выполняется один раз и не повторяется при новом входе в блок. Объекты класса static имеют локальную область действия (блок) и глобальное время жизни (время выполнения программы).
Пример. Использование статических переменных.
Программа:
void example (int c); /* прототип функции */
void main ( ) /* главная функция */
{ int count; /* локальная переменная блока */
for (count=9; count >= 5; count -= 2) /* цикл счетчика */
example (count); /* вызов функции */
}
void example (int c) /* заголовок функции */
{ int f=1; /* локальная переменная */
static int stat=1; /* статическая переменная */
printf (“c=%d, f=%d, stat=%d\n”, c, f ,stat);
stat++; /* изменение статической переменной */
}
Результаты программы: c=9, f=1, stat=1
c=7, f=1, stat=2
c=5, f=1, stat=3