Время жизни и область видимости переменной
Лабораторная работа №4
Программирование пользовательских функций.
Цель работы:
1. Изучить методы создания и использования функций.
Теоретические сведения.
Функцией называется выделенная последовательность инструкций, предназначенных для решения определенной задачи. Ранее мы уже использовали библиотечные функции ввода-вывода printf() и scanf(), в данной лабораторной работе познакомимся с правилами создания своих (пользовательских) функций.
Есть несколько причин для использования пользовательских функций, во-первых, программа приобретает некоторую структуру и, тем самым, становится более понятной и упорядоченной, во-вторых, исключаются повторы похожих участков текста, то есть текст программы оптимизируется.
Функция может многократно вызываться из различных частей программы, в
общем случае она выполняет следующие действия:
· выполняет инструкции, согласно заложенному алгоритму;
· может возвращать результат в вызывающую программу.
Прежде, чем продолжать необходимо ввести несколько новых понятий, которые тесно связаны с использованием функций – это время жизни и область видимости программных объектов.
Время жизни и область видимости переменной.
Время жизни и область видимости переменной связаны с блоком программы, в языке С различают два вида программных блоков:
Глобальная переменная существует на протяжении всего времени выполнения программы.
Локальная переменная существует только во время выполнения блока, в котором она определена.
Переменная может быть видима в пределах:
· во всех модулях (если программа располагается в нескольких файлах).
Область видимости переменной зависит от того, в каком месте программы (на каком уровне) она объявлена. Если переменная объявляется вне всех блоков программы (обычно вначале программы, до функции main()), то это внешнее объявление (глобальная переменная).
Внутри блока или функции объявляются локальные переменные.
При каждом входе в блок (или функцию) локальная переменная создается (для неё выделяется память), а при выходе из блока переменная уничтожается.
Пример 1:
int q=0; // глобальная переменная q
q++;// глобальные переменные доступны во всех блоках и функциях
int k,l; // локальные переменные вложенного блока
i++; // локальные переменные из объемлющего блока (функция main) доступны во вложенном блоке
Общие сведения о времени существования переменных
Время, в течение которого переменная сохраняет свое значение, называется временем жизни. За время жизни значение переменной может изменяться, но она всегда содержит некоторое значение. Когда переменная теряет область, она перестает содержать значение.
При запуске процедуры выполняется инициализация всех переменных. Числовая переменная инициализирована до нуля, строка переменной длины инициализирована в строку нулевой длины («»), а строка фиксированной длины заполняется символом, представленным кодом символов ASCII 0 или Chr(0). Переменные типа Variant при инициализации получают значение Empty. Каждый элемент переменной пользовательского инициализируется как отдельная переменная.
Когда вы объявляете переменнуюобъекта, пространство зарезервировано в памяти, но его значение задалось nothing, пока вы не назначите ссылку на объект с помощью параметра Set.
Если значение переменной не изменяется во время выполнения кода, она сохраняет назначенное при инициализации значение до потери области.
Переменная уровня процедуры, объявленная с помощью параметра Dim, сохраняет значение до завершения процедуры. Если процедура вызывает другие процедуры, переменная сохраняет свое значение и во время выполнения этих процедур.
Если переменная на уровне процедур объявляется с помощью ключевого слова Static, переменная сохраняет свое значение до тех пор, пока код работает в любом модуле. После завершения выполнения всего кода, переменная теряет область и значение. Ее время жизни аналогично времени жизни переменной уровня модулей.
Переменная уровня модулей отличается от статической переменной. В стандартном модуле или модуле класса она сохраняет свое значение до остановки выполнения кода. В модуле класса она сохраняет значение, пока существует экземпляр этого класса. Переменные уровня модулей использую ресурсы памяти до сброса их значений, поэтому такие переменные следует использовать только по необходимости.
Если вы включаете ключевое слово Static перед заявлением Sub или Function, между вызовами сохраняются значения всех переменных уровня процедуры в процедуре.
См. также
Поддержка и обратная связь
Есть вопросы или отзывы, касающиеся Office VBA или этой статьи? Руководство по другим способам получения поддержки и отправки отзывов см. в статье Поддержка Office VBA и обратная связь.
Время жизни переменных в программировании
По окончании этой главы должно сложиться более-менее цельное представление о программе, написанной в традиционной технологии программирования: это иерархия типов данных, порожденных ими переменных и набор вызывающих друг друга функций. При небольшом объеме проекта все это можно «свалить в кучу». Однако имеющиеся средства организации процесса трансляции в Си (см. 1.7) позволяют навести здесь элементарный порядок. Посмотрим, как в традиционной технологии приживается принцип модульной организации программы – разделение исходного текста на отдельные файлы с независимой трансляцией и последующей сборкой (компоновкой). Но для начала необходимо разобраться со свойствами переменных и функций, имеющих отношение к модульности.
Прежде всего, в модульном проектировании программист сталкивается с двумя принципиально различными по назначению конструкциями, которые в Си синтаксически похожи: определение и объявление.
Определение объекта должно производиться в программе только один раз, а соответствующий ему внутренний программный код размещается в создаваемом при трансляции объектом модуле.
Объявлений одного и того же объекта в программе может быть сколько угодно, сами они не порождают внутреннего программного кода, а только помогают сформировать корректное обращение к объявляемому объекту.
Понятие доступности (известности, видимости) опять-таки относит нас к организации процесса трансляции. Поэтому можно указать, как минимум, две причины, по которым возникает необходимость в объявлениях:
· определение переменной или функции находится в другом модуле (файле);
· определение переменной или функции находится «вниз по тексту». Большинство трансляторов «не заглядывает вперед» в поисках определений, поэтому если обращение производится до определения объекта, то он нуждается в дополнительном объявлении.
Время жизни и область действия переменных
В зависимости от способа и места своего определения переменные обладают рядом свойств, от которых зависит функциональное назначение переменной в программе. Это время жизни и область действия.
1. переменная создается функцией в стеке в момент начала выполнения функции и уничтожается при выходе из нее, переменная существует «от скобки до скобки». Местом размещения таких переменных является стек программы (сегмент стека) ;
1. тело функции или блока, то есть «от скобки до скобки»;
2. текущий модуль от места определения или объявления переменной до конца модуля, то есть в текущем файле;
3. все модули программы, проект в целом.
Прежде чем перечислить виды переменных (классы памяти), имеющиеся в Си, отметим синтаксическую разницу между определением и объявлением переменной.
Итак, в зависимости от сочетания основных свойств – времени жизни и области действия – имеют место переменные различных видов (различных классов памяти).
Синтаксис определения: любая переменная, определенная в начале тела функции или блока, по умолчанию является автоматической.
Синтаксис определения: любая переменная, определенная вне тела функции, по умолчанию является внешней.
Несмотря на то, что внешняя переменная потенциально доступна из любого модуля, сам факт ее существования должен быть известен транслятору. Если переменная определена в модуле, то она доступна от точки определения до конца файла. В других модулях требуется произвести объявление внешней переменной.
Определение переменной должно производиться только в одном модуле, при трансляции которого она создается и в котором размещается. Соответствие типов переменных в определении и объявлениях транслятором не может быть проверено. Ответственность за это соответствие ложится целиком на программиста.
Статические переменные. Имеют сходные с внешними переменными характеристики времени жизни («всегда») и размещения в памяти (сегмент данных), но ограниченную область действия.
Статические переменные, определенные вне функции (свойства 2,2) имеют область действия, ограниченную текущим модулем. Они предназначены для создания собственных переменных модуля, которые не должны быть «видны» извне, чтобы не вступать в конфликт с одноименными внешними переменными в других модулях.
Определение и объявление функций
Указанное свойство приводит к тому, что любая функция, даже созданная «для внутреннего употребления» попадает в пространство имен проекта, поэтому в других модулях определить функции с таким именем уже нельзя. Чтобы не засорять проект лишними именами, рекомендуется определять функции как статические. Это делается добавлением слова static перед заголовком функции. В результате чего область действия функции сужается и она становится доступной (видимой) только в текущем модуле (файле).
Синтаксис определения и объявления функций отличается значительным разнообразием, поэтому требует отдельного рассмотрения.
int clrscr (); // без контроля соответствия (анахронизм)
int clrscr ( void ); // без параметров
int strcmp(char*, char*);
extern int strcmp(); // без контроля соответствия (анахронизм)
extern int strcmp(char*, char*);
Организация проекта в классической технологии
Возможность объявлений переменных и функций позволяют любым способом разбить программу на модули (файлы), используя взаимные объявления. Но все-таки желательно, чтобы структура проекта отражала логическую организацию программы. На этот счет имеются следующие рекомендации :
· модуль (файл) должен содержать определения внешних (глобальных) переменных и функций, имеющих общее назначение, работающих с одинаковыми структурами (и типами) данных. Под такое определение подходит понятие библиотека функций. Возможны и другие основания для объединения ;
· для того, чтобы обратиться к объекту (например, вызвать функцию) из другого модуля, необходимо директивой include подключить из текущего каталога заголовочный файл того модуля, где локализован объект.
// Заголовочный файл библиотеки
typedef MyData *PData, MData[];
extern PData DM ; // Объявление собственных переменных
extern void add ( PData ); // Объявление функций модуля
extern PData remove();
// Модуль исходного текста библиотеки
PData DM ; // Определение собственных переменных
void add(PData pp) <… тело …>// Определение функций
// Набор разнородных функций
PData q = new MyData ; // и вызова библиотечных функций
extern void gogo (); // Отдельное объявление для внешней функции
void where _ you (); // Объявление для ссылки вперед
gogo (); // Вызов внешней функции
where _ you (); // Вызов функции «вниз по течению»
Вопросы без ответов
Определите, где задано определение, объявление и вызов функции.
Файлы и переменные
Общие данные
Первый вариант состоит в создании объекта класса RandomGenerator в функции main и передаче ссылки на него во все функции и методы, где он потребуется.
Поскольку функция main завершает работу программы, все необходимые условия выполнены: генератор случайных чисел создается в самом начале программы, все объекты и функции обращаются к одному и тому же генератору, и генератор уничтожается по завершении программы. Такой стиль программирования допустимо использовать только в том случае, если передавать ссылку на используемый экземпляр объекта требуется нечасто. В противном случае этот способ крайне неудобен. Передавать ссылку на один и тот же объект утомительно, к тому же это загромождает интерфейс классов.
Глобальные переменные
Объявление внешней переменной можно поместить в файл-заголовок. Тогда не нужно будет повторять объявление переменной с описателем extern в каждом файле, который ее использует.
Повышение надежности обращения к общим данным
Определять глобальную переменную намного удобнее, чем передавать ссылку на генератор случайных чисел в каждый метод и функцию в качестве аргумента. Достаточно описать внешнюю глобальную переменную (включив соответствующий файл заголовков с помощью оператора #include ), и генератор становится доступен. Не нужно менять интерфейс, если вдруг понадобится обратиться к генератору. Не следует передавать один и тот же объект в разные функции.
При составлении программ самым лучшим решением будет то, которое не позволит ошибиться, т.е. неправильная программа не будет компилироваться. Не всегда это возможно, но в данном случае, как и во многих других, соответствующие средства имеются в языке Си++.
Изменим описание класса RandomGenerator :
Теперь программа, использующая генератор случайных чисел, будет выглядеть так:
Разумеется, существуют и другие способы сделать так, чтобы существовал только один объект какого-либо класса.
Кратко суммируем результаты этого параграфа:
Область видимости имен
Между именами переменных, функций, типов и т.п. при использовании одного и того же имени в разных частях программы могут возникать конфликты. Для того чтобы эти конфликты можно было разрешать, в языке существует такое понятие как область видимости имени.
Объявление может перекрывать такое же имя, объявленное во внешней области.
Следующие обращения допустимы извне класса:
При реализации метода Method обращения к тем же именам могут быть проще:
При попытке обратиться извне класса к атрибуту набора BLUE компилятор выдаст ошибку, поскольку имя BLUE определено только в контексте класса.
Оператор определения контекста namespace
Контексты могут быть вложенными, соответственно, имя должно быть квалифицировано несколько раз:
Время жизни и область видимости программных объектов
Время жизни переменной (глобальной или локальной) определяется по следующим правилам.
1. Переменная, объявленная глобально (т.е. вне всех блоков), существует на протяжении всего времени выполнения программы.
2. Локальные переменные (т.е. объявленные внутри блока) с классом памяти register или auto, имеют время жизни только на период выполнения того блока, в котором они объявлены. Если локальная переменная объявлена с классом памяти static или extern, то она имеет время жизни на период выполнения всей программы.
Видимость переменных и функций в программе определяется следующими правилами.
1. Переменная, объявленная или определенная глобально, видима от точки объявления или определения до конца исходного файла. Можно сделать переменную видимой и в других исходных файлах, для чего в этих файлах следует ее объявить с классом памяти extern.
2. Переменная, объявленная или определенная локально, видима от точки объявления или определения до конца текущего блока. Такая переменная называется локальной.
3. Переменные из объемлющих блоков, включая переменные объявленные на глобальном уровне, видимы во внутренних блоках. Эту видимость называют вложенной. Если переменная, объявленная внутри блока, имеет то же имя, что и переменная, объявленная в объемлющем блоке, то это разные переменные, и переменная из объемлющего блока во внутреннем блоке будет невидимой.
4. Функции с классом памяти static видимы только в исходном файле, в котором они определены. Всякие другие функции видимы во всей программе.
Метки в функциях видимы на протяжении всей функции.
Имена формальных параметров, объявленные в списке параметров прототипа функции, видимы только от точки объявления параметра до конца объявления функции.



