Время жизни и область видимости переменной
Лабораторная работа №4
Программирование пользовательских функций.
Цель работы:
1. Изучить методы создания и использования функций.
Теоретические сведения.
Функцией называется выделенная последовательность инструкций, предназначенных для решения определенной задачи. Ранее мы уже использовали библиотечные функции ввода-вывода printf() и scanf(), в данной лабораторной работе познакомимся с правилами создания своих (пользовательских) функций.
Есть несколько причин для использования пользовательских функций, во-первых, программа приобретает некоторую структуру и, тем самым, становится более понятной и упорядоченной, во-вторых, исключаются повторы похожих участков текста, то есть текст программы оптимизируется.
Функция может многократно вызываться из различных частей программы, в
общем случае она выполняет следующие действия:
· выполняет инструкции, согласно заложенному алгоритму;
· может возвращать результат в вызывающую программу.
Прежде, чем продолжать необходимо ввести несколько новых понятий, которые тесно связаны с использованием функций – это время жизни и область видимости программных объектов.
Время жизни и область видимости переменной.
Время жизни и область видимости переменной связаны с блоком программы, в языке С различают два вида программных блоков:
Глобальная переменная существует на протяжении всего времени выполнения программы.
Локальная переменная существует только во время выполнения блока, в котором она определена.
Переменная может быть видима в пределах:
· во всех модулях (если программа располагается в нескольких файлах).
Область видимости переменной зависит от того, в каком месте программы (на каком уровне) она объявлена. Если переменная объявляется вне всех блоков программы (обычно вначале программы, до функции main()), то это внешнее объявление (глобальная переменная).
Внутри блока или функции объявляются локальные переменные.
При каждом входе в блок (или функцию) локальная переменная создается (для неё выделяется память), а при выходе из блока переменная уничтожается.
Пример 1:
int q=0; // глобальная переменная q
q++;// глобальные переменные доступны во всех блоках и функциях
int k,l; // локальные переменные вложенного блока
i++; // локальные переменные из объемлющего блока (функция main) доступны во вложенном блоке
6) Классы памяти, область действия, область видимости, время жизни переменных
[класс памяти] [const] тип имя [инициализатор];
Такую переменную называют именованной константой, или просто константой. (Отличается от константы – литерала тем, что имеет имя).
Инициализатор – присвоение значений переменным, именованным константам. При описании можно присвоить переменной начальное значение, это называется инициализацией. Инициализатор можно записывать в двух формах — со знаком равенства:
или в круглых скобках:
Константа должна быть инициализирована при объявлении. В одном операторе можно описать несколько переменных одного типа, разделяя их запятыми.
short int а = 1; // целая переменная а
const char С = ‘ С ‘; // символьная константа С
char s. sf = ‘f ‘; // инициализация относится только к sf
char t (54); // либо t=54
float с = 0.22, x(3), sum;
Если тип инициализирующего значения не совпадает с типом переменной, выполняются преобразования типа по определенным правилам.
Описание переменной, кроме типа и класса памяти, явно или по умолчанию задает ее область действия. Класс памяти и область действия зависят не только от собственно описания, но и от места его размещения в тексте программы.
Область действия идентификатора — это часть программы, в которой его можно использовать для доступа к связанной с ним области памяти. В зависимости от области действия переменная может быть локальной или глобальной. Если переменная определена внутри блока (блок ограничен фигурными скобками), она называется локальной, область ее действия — от точки описания до конца блока, включая все вложенные блоки. Если переменная определена вне любого блока, она называется глобальной и областью ее действия считается файл, в котором она определена, от точки описания до его конца.
Класс памяти определяет время жизни и область видимости программного объекта (в частности, переменной). Если класс памяти не указан явным образом, он определяется компилятором исходя из контекста объявления.
Время жизни может быть постоянным (в течение выполнения программы) и временным (в течение выполнения блока).
Для задания класса памяти используются следующие спецификаторы:
auto — автоматическая переменная. Память под нее выделяется в стеке и при необходимости инициализируется каждый раз при выполнении оператора, содержащего ее определение. Освобождение памяти происходит при выходе из блока, в котором описана переменная. Время ее жизни — с момента описания до конца блока. Для глобальных переменных этот спецификатор не используется, а для локальных он принимается по умолчанию, поэтому задавать его явным образом большого смысла не имеет.
extern — означает, что переменная определяется в другом месте программы
(в другом файле или дальше по тексту). Используется для создания переменных, доступных во всех модулях программы, в которых они объявлены.
static — статическая переменная. Время жизни — постоянное. Инициализируется один раз при первом выполнении оператора, содержащего определение переменной.
В зависимости от расположения оператора описания статические переменные могут быть глобальными и локальными. Глобальные статические переменные видны только в том модуле, в котором они описаны.
register — аналогично auto, но память выделяется по возможности в регистрах процессора. Если такой возможности у компилятора нет, переменные обрабатываются как auto.
Если при определении начальное значение переменных явным образом не задается, компилятор присваивает глобальным и статическим переменным нулевое значение соответствующего типа. Автоматические переменные не инициализируются.
Имя переменной должно быть уникальным в своей области действия (например, в одном блоке не может быть двух переменных с одинаковыми именами).
Описание переменной может выполняться в форме объявления или определения. Объявление информирует компилятор о типе переменной и классе памяти, а определение содержит, кроме этого, указание компилятору выделить память в соответствии с типом переменной. В C++ большинство объявлений являются одновременно и определениями. В приведенном выше примере только описание 3 является объявлением, но не определением.
Переменная может быть объявлена многократно, но определена только в одном месте программы, поскольку объявление просто описывает свойства переменной, а определение связывает ее с конкретной областью памяти.
Классы памяти, время жизни и область видимости программных объектов. Инициализация локальных и глобальных переменных.
Время жизни и область видимости программных объектов
Время жизни переменной (глобальной или локальной) определяется по следующим правилам.
1. Переменная, объявленная глобально (т.е. вне всех блоков), существует на протяжении всего времени выполнения программы.
2. Локальные переменные (т.е. объявленные внутри блока) с классом памяти register или auto, имеют время жизни только на период выполнения того блока, в котором они объявлены. Если локальная переменная объявлена с классом памяти static или extern, то она имеет время жизни на период выполнения всей программы.
Видимость переменных и функций в программе определяется следующими правилами.
1. Переменная, объявленная или определенная глобально, видима от точки объявления или определения до конца исходного файла. Можно сделать переменную видимой и в других исходных файлах, для чего в этих файлах следует ее объявить с классом памяти extern.
2. Переменная, объявленная или определенная локально, видима от точки объявления или определения до конца текущего блока. Такая переменная называется локальной.
3. Переменные из объемлющих блоков, включая переменные объявленные на глобальном уровне, видимы во внутренних блоках. Эту видимость называют вложенной. Если переменная, объявленная внутри блока, имеет то же имя, что и переменная, объявленная в объемлющем блоке, то это разные переменные, и переменная из объемлющего блока во внутреннем блоке будет невидимой.
4. Функции с классом памяти static видимы только в исходном файле, в котором они определены. Всякие другие функции видимы во всей программе.
Метки в функциях видимы на протяжении всей функции.
Имена формальных параметров, объявленные в списке параметров прототипа функции, видимы только от точки объявления параметра до конца объявления функции.
Инициализация глобальных и локальных переменных
При инициализации необходимо придерживаться следующих правил:
1. Объявления содержащие спецификатор класса памяти extern не могут содержать инициаторов.
2. Глобальные переменные всегда инициализируются, и если это не сделано явно, то они инициализируются нулевым значением.
3. Переменная с классом памяти static может быть инициализирована константным выражением. Инициализация для них выполняется один раз перед началом программы. Если явная инициализация отсутствует, то переменная инициализируется нулевым значением.
4. Инициализация переменных с классом памяти auto или register выполняется всякий раз при входе в блок, в котором они объявлены. Если инициализация переменных в объявлении отсутствует, то их начальное значение не определено.
5. Начальными значениями для глобальных переменных и для переменных с классом памяти static должны быть константные выражения. Адреса таких переменных являются константами и эти константы можно использовать для инициализации объявленных глобально указателей. Адреса переменных с классом памяти auto или register не являются константами и их нельзя использовать в инициаторах.
Класс памяти
Класс памятиопределяет порядок размещения объекта в памяти.
Различают автоматический и статический классы памяти. C располагает четырьмя спецификаторами класса памяти:
Спецификаторы позволяют определить класс памяти определяемого объекта:
Этот спецификатор автоматического класса памяти указывает на то, что объект располагается в локальной (или автоматически распределяемой) памяти. Он используется в операторах объявления в теле функций, а также внутри блоков операторов, ограниченных фигурными скобками.
Объекты, имена которых объявляются со спецификатором auto, размещаются в локальной памяти непосредственно перед началом выполнения функции или блока операторов, ограниченного фигурными скобками <>. При этом размер выделяемой памяти известен ещё на этапе компиляции программы, поэтому при компиляции могут быть применены специальные процедуры, оптимизирующие выделение памяти. Такая процедура выделения памяти называется статическим распределением памяти(не путать со статическим классом памяти).
При выходе из блока или при возвращении из функции, соответствующая область локальной памяти освобождается и все ранее размещённые в ней объекты уничтожаются. Таким образом спецификатор влияет на время жизни объекта (это время локально). Спецификатор auto используется редко, поскольку все объекты, определяемые непосредственно в теле функции или в блоке операторов и так по умолчанию имеют автоматический класс хранения и располагаются в локальной памяти. Класс хранения таких объектов, как аргументы функций, также называется автоматическим. Такое название означает, что область памяти для хранения этих элементов данных выделяется автоматически при вызове функции и также автоматически освобождается, когда исполнение этой функции завершается, т. е. временем жизни аргументов является продолжительность исполнения функции. Как только функция завершает работу, их значения будут утеряны.
Пример использования: auto int i;
Ещё один спецификатор автоматического класса памяти. Применяется к объектам, по умолчанию располагаемым в локальной памяти. Представляет из себя «ненавязчивую просьбу» к транслятору (если это возможно) о размещении значений объектов, объявленных со спецификатором register в одном из доступных регистров, а не в локальной памяти. В этом случае доступ к таким переменным осуществляется гораздо быстрее. Если по какой-либо причине в момент начала выполнения кода в данном блоке операторов регистры оказываются занятыми, транслятор обеспечивает с этими объектами обращение, как с объектами класса auto. Очевидно, что в этом случае объект располагается в локальной области памяти. Область действия и время жизни полностью идентичны классу auto.
Пример использования: register int i;
Спецификатор внутреннего статического класса памяти. Применяется только(!) к именам объектов и функций (не применяется к аргументам функций). В C++ этот спецификатор имеет два значения.
Первое означает, что определяемый объект располагается по фиксированному адресу. Тем самым обеспечивается существование объекта с момента его определения до конца выполнения программы (время жизни – всё время работы программы, хотя переменная и может быть определена локально). В отличие от объекта класса памяти auto, объект не будет уничтожаться при выходе из функции, где он был объявлен.
Например, при каждом вызове функции IncS(), объявленной следующим образом:
значение переменной f в начале выполнения функции будет равно 1, в ходе выполнения функции оно увеличится на 1, а в конце выполнения f уничтожится. Значение же переменной s будет увеличиваться на 1 при каждом вызове функции, т.к. переменная s является статической и не уничтожается на протяжении всей программы. Отметим также, что при такой записи значение 1 присваивается переменной s только один раз – при инициализациив момент создания переменной. При последующих вызовах функции эта строчка игнорируется.
Второе значение спецификатора static означает локальность. Объявленая со спецификатором static переменная или функция локальна в одном программном модуле (то есть, недоступна из других модулей многомодульной программы). Может использоваться в объявлениях как внутри блоков и функций, так и вне их (в этом случае мы получаем внешнюю статическую переменную).
Спецификатор внешнего статического класса памяти. Спецификатор extern позволяет функции использовать внешнюю переменную, даже, если она определяется позже в этом или другом модуле.
Внешниминазываются переменные, объявленные вне функции. Поскольку при таком описании к ним автоматически получают доступ все функции этого модуля, то таки переменные также называют глобальными. С помощью спецификатора extern можно получить доступ к внешней переменно и из другого модуля, для чего они чаще всего и применяются.
Extern является своего рода модификатором-ссылкой, указывающим на то, что где-то такая переменная уже описана. И поэтому в процессе компиляции не следует выделять под неё память именно здесь, а следует найти такую внешнюю переменную в других модулях на стадии компоновки. Если такая переменная найдена не будет, то компоновщик выдаст ошибку «Объект не найден».
Module MAIN.CPP
/* определить внешние данные*/
extern int iValue1;
extern int iValue2;
printf («значения равны: %d %d \n», iValue1, iValue2);
Время жизни области видимости и классы памяти переменных
Обычная СИ-программа представляет собой определение функции main, которая для выполнения необходимых действий вызывает другие функции. Приведенные выше примеры программ представляли собой один исходный файл, содержащий все необходимые для выполнения программы функции. Связь между функциями осуществлялась по данным посредством передачи параметров и возврата значений функций. Но компилятор языка СИ позволяет также разбить программу на несколько отдельных частей (исходных файлов), оттранслировать каждую часть отдельно, и затем объединить все части в один выполняемый файл при помощи редактора связей.
При такой структуре исходной программы функции, находящиеся в разных исходных файлах могут использовать глобальные внешние переменные. Все функции в языке Си по определению внешние и всегда доступны из любых файлов. Например, если программа состоит из двух исходных файлов, как показано на рис.2., то функция main может вызывать любую из трех функций fun1, fun2, fun3, а каждая из этих функций может вызывать любую другую.
| . | ||
| file1.c | file2.c | |
|---|---|---|
| Рис.2. Пример программы из двух файлов | ||
Для того, чтобы определяемая функция могла выполнять какие либо действия, она должна использовать переменные. В языке СИ все переменные должны быть объявлены до их использования. Объявления устанавливают соответствие имени и атрибутов переменной, функции или типа. Определение переменной вызывает выделение памяти для хранения ее значения. Класс выделяемой памяти определяется спецификатором класса памяти, и определяет время жизни и область видимости переменной, связанные с понятием блока программы.
Все функции в СИ имеют глобальное время жизни и существуют в течение всего времени выполнения программы.
Если объект объявлен внутри блока, то он видим в этом блоке, и во всех внутренних блоках. Если объект объявлен на внешнем уровне, то он видим от точки его объявления до конца данного исходного файла.
Объект может быть сделан глобально видимым с помощью соответствующих объявлений во всех исходных файлах, образующих программу.
Спецификатор класса памяти в объявлении переменной может быть auto, register, static или extern. Если класс памяти не указан, то он определяется по умолчанию из контекста объявления.
Объекты классов auto и register имеют локальное время жизни. Спецификаторы static и extern определяют объекты с глобальным временем жизни.
При объявлении переменной на внутреннем уровне может быть использован любой из четырех спецификаторов класса памяти, а если он не указан, то подразумевается класс памяти auto.
Переменная с классом памяти auto имеет локальное время жизни и видна только в блоке, в котором объявлена. Память для такой переменной выделяется при входе в блок и освобождается при выходе из блока. При повторном входе в блок этой переменной может быть выделен другой участок памяти.
Переменная с классом памяти auto автоматически не инициализируется. Она должна быть проинициализирована явно при объявлении путем присвоения ей начального значения. Значение неинициализированной переменной с классом памяти auto считается неопределенным.
Спецификатор класса памяти register предписывает компилятору распределить память для переменной в регистре, если это представляется возможным. Использование регистровой памяти обычно приводит к сокращению времени доступа к переменной. Переменная, объявленная с классом памяти register, имеет ту же область видимости, что и переменная auto. Число регистров, которые можно использовать для значений переменных, ограничено возможностями компьютера, и в том случае, если компилятор не имеет в распоряжении свободных регистров, то переменной выделяется память как для класса auto. Класс памяти register может быть указан только для переменных с типом int или указателей с размером, равным размеру int.
Переменные, объявленные на внутреннем уровне со спецификатором класса памяти static, обеспечиваю возможность сохранить значение переменной при выходе из блока и использовать его при повторном входе в блок. Такая переменная имеет глобальное время жизни и область видимости внутри блока, в котором она объявлена. В отличие от переменных с классом auto, память для которых выделяется в стеке, для переменных с классом static память выделяется в сегменте данных, и поэтому их значение сохраняется при выходе из блока.
В приведенном примере объявлены три разные переменные с классом памяти static, имеющие одинаковые имена i. Каждая из этих переменных имеет глобальное время жизни, но видима только в том блоке (функции), в которой она объявлена. Эти переменные можно использовать для подсчета числа обращений к каждой из трех функций.
Переменные класса памяти static могут быть инициализированы константным выражением. Если явной инициализации нет, то такой переменной присваивается нулевое значение. При инициализации константным адресным выражением можно использовать адреса любых внешних объектов, кроме адресов объектов с классом памяти auto, так как адрес последних не является константой и изменяется при каждом входе в блок. Инициализация выполняется один раз при первом входе в блок.
Переменная, объявленная локально с классом памяти extern, является ссылкой на переменную с тем же самым именем, определенную глобально в одном из исходных файлов программы. Цель такого объявления состоит в том, чтобы сделать определение переменной глобального уровня видимым внутри блока.
Объявление переменной i[] как extern в приведенном примере делает ее видимой внутри функции fun1. Определение этой переменной находится в файле file2.c на глобальном уровне и должно быть только одно, в то время как объявлений с классом памяти extern может быть несколько.
Объявление с классом памяти extern требуется при необходимости использовать переменную, описанную в текущем исходном файле, но ниже по тексту программы, т.е. до выполнения ее глобального определения. Следующий пример иллюстрирует такое использование переменной с именем st.
Объявление переменной со спецификатором extern информирует компилятор о том, что память для переменной выделять не требуется, так как это выполнено где-то в другом месте программы.
При объявлении переменных на глобальном уровне может быть использован спецификатор класса памяти static или extern, а так же можно объявлять переменные без указания класса памяти. Классы памяти auto и register для глобального объявления недопустимы.
1. Переменная объявлена с классом памяти static. Такая переменная может быть инициализирована явно константным выражением, или по умолчанию нулевым значением. То есть обявления static int i=0 и static int i эквивалентны, и в обоих случаях переменной i будет присвоено значение 0.
2. Переменная объявлена без указания класса памяти, но с явной инициализацией. Такой переменной по умолчанию присваивается класс памяти static. То есть объявления int i=1 и static int i=1 будут эквивалентны.
Переменная объявленная глобально видима в пределах остатка исходного файла, в котором она определена. Выше своего описания и в других исходных файлах эта переменная невидима (если только она не объявлена с классом extern).
Глобальная переменная может быть определена только один раз в пределах своей области видимости. В другом исходном файле может быть объявлена другая глобальная переменная с таким же именем и с классом памяти static, конфликта при этом не возникает, так как каждая из этих переменных будет видимой только в своем исходном файле.
Спецификатор класса памяти extern для глобальных переменных используется, как и для локального объявления, в качестве ссылки на переменную, объявленную в другом месте программы, т.е. для расширения области видимости переменной. При таком объявлении область видимости переменной расширяется до конца исходного файла, в котором сделано объявление.
В объявлениях с классом памяти extern не допускается инициализация, так как эти объявления ссылаются на уже существующие и определенные ранее переменные.
Переменная, на которую делается ссылка с помощью спецификатора extern, может быть определена только один раз в одном из исходных файлов программы.
1.6.2. Объявления функций
Функции всегда определяются глобально. Они могут быть объявлены с классом памяти static или extern. Объявления функций на локальном и глобальном уровнях имеют одинаковый смысл.
Правила определения области видимости для функций отличаются от правил видимости для переменных и состоят в следующем.
1. Функция, объявленная как static, видима в пределах того файла, в котором она определена. Каждая функция может вызвать другую функцию с классом памяти static из своего исходного файла, но не может вызвать функцию определенную с классом static в другом исходном файле. Разные функции с классом памяти static имеющие одинаковые имена могут быть определены в разных исходных файлах, и это не ведет к конфликту.
2. Функция, объявленная с классом памяти extern, видима в пределах всех исходных файлов программы. Любая функция может вызывать функции с классом памяти extern.
3. Если в объявлении функции отсутствует спецификатор класса памяти, то по умолчанию принимается класс extern.
Все объекты с классом памяти extern компилятор помещает в объектном файле в специальную таблицу внешних ссылок, которая используется редактором связей для разрешения внешних ссылок. Часть внешних ссылок порождается компилятором при обращениях к библиотечным функциям СИ, поэтому для разрешения этих ссылок редактору связей должны быть доступны соответствующие библиотеки функций.
1.6.3. Время жизни и область видимости программных объектов
Время жизни переменной (глобальной или локальной) определяется по следующим правилам.
1. Переменная, объявленная глобально (т.е. вне всех блоков), существует на протяжении всего времени выполнения программы.
2. Локальные переменные (т.е. объявленные внутри блока) с классом памяти register или auto, имеют время жизни только на период выполнения того блока, в котором они объявлены. Если локальная переменная объявлена с классом памяти static или extern, то она имеет время жизни на период выполнения всей программы.
Видимость переменных и функций в программе определяется следующими правилами.
1. Переменная, объявленная или определенная глобально, видима от точки объявления или определения до конца исходного файла. Можно сделать переменную видимой и в других исходных файлах, для чего в этих файлах следует ее объявить с классом памяти extern.
2. Переменная, объявленная или определенная локально, видима от точки объявления или определения до конца текущего блока. Такая переменная называется локальной.
3. Переменные из объемлющих блоков, включая переменные объявленные на глобальном уровне, видимы во внутренних блоках. Эту видимость называют вложенной. Если переменная, объявленная внутри блока, имеет то же имя, что и переменная, объявленная в объемлющем блоке, то это разные переменные, и переменная из объемлющего блока во внутреннем блоке будет невидимой.
4. Функции с классом памяти static видимы только в исходном файле, в котором они определены. Всякие другие функции видимы во всей программе.
Метки в функциях видимы на протяжении всей функции.
Имена формальных параметров, объявленные в списке параметров прототипа функции, видимы только от точки объявления параметра до конца объявления функции.
1.6.4. Инициализация глобальных и локальных переменных
При инициализации необходимо придерживаться следующих правил:
1. Объявления содержащие спецификатор класса памяти extern не могут содержать инициаторов.
2. Глобальные переменные всегда инициализируются, и если это не сделано явно, то они инициализируются нулевым значением.
3. Переменная с классом памяти static может быть инициализирована константным выражением. Инициализация для них выполняется один раз перед началом программы. Если явная инициализация отсутствует, то переменная инициализируется нулевым значением.
4. Инициализация переменных с классом памяти auto или register выполняется всякий раз при входе в блок, в котором они объявлены. Если инициализация переменных в объявлении отсутствует, то их начальное значение не определено.
5. Начальными значениями для глобальных переменных и для переменных с классом памяти static должны быть константные выражения. Адреса таких переменных являются константами и эти константы можно использовать для инициализации объявленных глобально указателей. Адреса переменных с классом памяти auto или register не являются константами и их нельзя использовать в инициаторах.
В приведенном примере глобальная переменная global_var имеет глобальное время жизни и постоянный адрес в памяти, и этот адрес можно использовать для инициализации статического указателя global_ptr. Локальная переменная local_var, имеющая класс памяти auto размещается в памяти только на время работы функции func, адрес этой переменной не является константой и не может быть использован для инициализации статической переменной local_ptr. Для инициализации локальной регистровой переменной reg_ptr можно использовать неконстантные выражения, и, в частности, адрес переменной local_ptr. [ Назад | Оглавление | Вперед ]



