Определение и описание переменных
В зависимости от способа определения переменной и его места размещения в программе явно, либо по умолчанию всегда определяются следующие её свойства:
· тип переменной;
· класс памяти (задаёт размещение переменной);
· продолжительность существования переменной;
· сфера действия имени переменной;
· видимость переменной;
· тип компоновки.
О типах и их свойствах, задаваемых при определении переменных, уже говорилось ранее. В общем виде определение переменных имеет следующий формат:
[s] [m] тип имя1 [иниц.1], имя2 [иниц.2], … ;
где s - спецификатор класса памяти ( auto, static, extern, register); m – модификатор const или volatile; тип – один из типов данных (основной или определенный программистом); имя – идентификатор; иниц. – необязательный инициализатор, определяющий начальное значение соответствующего объекта. В квадратных скобках указаны параметры, которые можно опустить при определении, задав тем самым их значения по умолчанию.
Синтаксис инициализатора (иниц.) переменной:
= инициализирующее выражение
либо
(инициализирующее выражение)
Пример:
const double pi = 3.1415;/*Определяется и инициализируется именованная статическая константа pi */
extern const double pi; // Описание константы pi
Наиболее часто в качестве инициализирующего выражения используется константа. Вторая «скобочная» форма инициализации разрешена только внутри функции, т.е. для локальных объектов. Инициализация в отличии от присваивания выполняется только один раз во время определения объекта.
Модификаторы:
volatile (переменный ) – ключевое слово (модификатор), которое сообщает, что значение переменной может быть изменено во время выполнения программы. Модификатор volatile практически никогда не употребляется в тексте программ, так как по умолчанию все переменные являются volatile.
const (постоянный) – ключевое слово (модификатор), которое сообщает, что значение переменной не может быть изменено во время выполнения программы. Модификатор const позволяет переопределить заданный по умолчанию при определении переменных модификатор volatile. Используется для создания именованных констант, например: const double PI = 3.14159;//определяется именованная константа PI.
Классы памяти:
auto – автоматически выделяемая, локальная память. Спецификатор auto может быть задан только при определении объектов блока, например, в теле функции. Этим объектам память выделяется при входе в блок и освобождается при выходе из него. Вне блока объекта класса auto не существует. Необходимость явно использовать спецификатор класса памяти auto в тексте программы практически всегда отсутствует, так как все переменные, определенные внутри блока по умолчанию, являются локальными, т.е. по умолчанию имеют спецификатор auto;
register – автоматически выделяемая, по возможности регистровая память. Спецификатор register аналогичен auto, но для размещения значений объектов используются регистры, а не участки основной памяти. Такая возможность имеется не всегда, и в случае отсутствия регистровой памяти (если регистры заняты другими данными) объект класса register компилятор обрабатывает как объект auto. Спецификатор register также практически не используется в тексте программ, так как компилятор очень часто сам оптимизирует код или по скорости выполнения, или по его размеру, тем самым самостоятельно решая какие переменные сделать register, а какие нет.
static – внутренний тип компоновки и статическая продолжительность существования. Объект, описанный со спецификатором static, будет существовать в пределах того файла с исходным текстом программы (модуля), где он определен. Класс памяти static может приписываться переменным и функциям. Спецификатор static используется в программах довольно часто, так как позволяет переопределить используемый по умолчанию для определенных внутри блока переменных спецификатор auto, тем самым сделав их статическими, а не локальными;
extern – внешний тип компоновки и статическая продолжительность существования. Объект класса extern глобален, т.е. доступен во всех модулях (файлах) программы. Класс extern может быть приписан переменной или функции. Спецификатор extern используется в многомодульных программах, так как позволяет явно определить внешний тип компоновки для статических переменных, которые по умолчанию имеют внутренний тип компоновки.
Переменные со статической продолжительностью существования существуют с момента запуска программы и до момента окончания её работы. Фактически, память под такие переменные отводится в сегменте данных программы, т.е. в исполняемом файле программы (*.exe) есть соответствующее типу переменной количество байт, в которых хранится значение статической переменной.
По определению статическими переменными являются переменные, определенные вне любого блока ({}), либо переменные, определенные в блоке со спецификатором static. Переменная, определенная вне любого блока ({}), называется также глобальнойи по умолчанию имеет класс памяти extern. Сфера действия имени глобальной переменной, другими словами, фрагмент программы, где можно с помощью имени обратится к переменной, у глобальной переменной совпадает с областью существования, т.е. является вся программа.
При запуске программы на выполнение помимо памяти, требуемой под выполняемую программу (exe – файл), необходима дополнительная память (стек, куча), которая запрашивается у операционной системы. В зависимости от выполняемого фрагмента программы количество дополнительно используемой памяти может становиться больше или меньше. Именно в этой дополнительной памяти создаются и уничтожаются автоматические переменные. Таким образом, продолжительность существования локальной автоматической переменной - это не всё время выполнения программы, а только время, в течение которого выполняется какой-то её фрагмент.
По определению любая переменная, определенная внутри блока ({}) без спецификатора static, является локальной автоматической, а со спецификатором static – локальной статической. Область существования локальной автоматической переменной ограничена, начиная с места определения её в программе и заканчивая концом блока, в котором эта локальная переменная определена, а областью существования локальной статической переменной является вся программа. Сфера действия имени у локальной статической и автоматической переменных совпадает. Она начинается с места их определения в программе и заканчивается концом блока, в котором эти локальные переменные определены. Помимо этого, формальные параметры в определении функции также являются локальными автоматическими переменными, область существования которых ограничена телом функции. Формальные параметры в описании функции – это тоже локальные автоматические переменные, область существования которых ограничена строкой программы, содержащей описание этой функции. Пример:
//Программа 3.1
#include "stdafx.h"
#include <iostream> //1
int pr = 7; /*2. Определение глобальной статической переменной pr. Область существования этой переменной pr вся программа. */
int sum(int pa, int pr); /*3. Описание функции sum(), в которой два формальных параметра: pa и pr. Область существования pa и pr только эта строка программы. */
void main(){ //4.
int pr = 8; /*5. Определение локальной переменной pr. Область существования pr c момента определения до конца тела функции main(), т.е. с 5 по 13 строки программы. */
std::cout<<"\n 1. sum(pr, 3) = "<<sum(pr, 3); /*6. Вызов функции sum()*/
{ //7. Начало нового блока
int pr = 9; /*8. Определение локальной переменной pr. Область существования pr c момента определения до конца блока, т.е. с 8 по 11 строки программы. */
std::cout<<"\n 2. sum(pr, 3) = "<<sum(pr, 3); /*9. Вызов функции sum()*/
std::cout<<"\n 3. sum(::pr, 3) = "<<sum(::pr, 3); /*10. Вызов функции sum()*/
} //11. Конец блока
std::cout<<"\n 4. sum(::pr, 3) = "<<sum(::pr, 3);/*12. Вызов функции sum()*/
getchar();
} //13. Конец тела функции main()
int sum(int z, int pr){ /*14. Определены две локальные переменные z и pr. Область существования – тело функции sum(), т.е. 14 – 18 строки программы. */
static int count = 0; /*15. Определение локальной статической переменной count. Область существования вся программа. */
count ++; //16.
return z+pr; //17.
}
В этой программе в строках 1, 2, 4, 7, 13 определены переменные pr. Все эти переменные абсолютно разные, они хранят свои значения в разных фрагментах памяти, область существования у них тоже разная, единственное, что их объединяет - это имя.
Когда определено несколько переменных с одним именем и области их существования частично или полностью совпадают, например, как в случае с переменной pr, то для того чтобы определить, к какой конкретно переменной происходит обращение, необходимо учитывать видимость этой переменной в программе. Поясним на примере переменной pr понятие видимости переменной в каждой точке программы (рис. 3.1)
Рис. 3.1. Видимость объектов, связанных с одним идентификатором (именем) в однофайловой программе
На рис. 3.1 прямоугольниками показаны области существования переменных pr. Стрелками обозначено, к какой конкретно переменной происходит обращение при использовании имени pr в программе. Таким образом, определение локальной переменной с таким же именем, как и у глобальной, делает невидимой глобальную переменную, но к ней можно получить доступ, если применить операцию указания области видимости (::), что и продемонстрировано в 10 и 12 строках. Интересен тот факт, что доступ к локальной переменной pr, определенной в 5 строке из строк с 7 по 11, невозможен никаким способом. Отметим также, что глобальные переменные видны во всей программе и поэтому их можно использовать в теле функций, передавая через них данные.
Переменная count – это статическая переменная, поэтому область её существования - вся программа, но так как она локальная, то сфера действия имени этой переменной заключена в пределах, начиная с её определения и до конца тела функции sum(), т.е. строки с 15 по 18. К этой переменной нельзя обратиться за пределами указанного диапазона.
Особенность использования локальных статических переменных заключена в том, что они не уничтожаются при выходе из функции, как локальные автоматические. Это значит, что в них функции могут хранить какие-то данные в промежутках между своими вызовами. Строка 15 фактически ни разу не выполняется в программе при вызове функции sum(), т.е. во время выполнения в строке 15 не создаётся и не инициализируется нулевым значением новая переменная. Она уже была создана и инициализирована нулевым значением на этапе компиляции. Поэтому при вызове функции sum() значение count не обнуляется в строке 15; в строке 16 оно увеличивается на единицу и хранится до следующего вызова sum(). Таким образом, с помощью локальной статической переменой count подсчитывается количество вызовов функции sum().
Ещё одним понятием, связанным с переменными (объектами), является тип компоновки. О типе (внутреннем или внешнем) компоновки переменных имеет смысл говорить только для глобальных переменных и в программе, которая состоит более чем из одного файла (модуля), так как по определению все локальные и глобальные переменные в однофайловой программе имеют внутренний тип компоновки.
Если программа состоит более чем из одного файла (модуля), то глобальная переменная, определенная в одном файле, может быть доступна в другом файле программы, а может быть и нет. Доступность глобальной переменной определяется типом компоновки. Внешний тип компоновки означает, что одна и та же глобальная переменная может быть видна в нескольких файлах программы; внутренний тип компоновки означает, что глобальная переменная видна только в том файле, в котором она определена. Пример:
//Программа 3.2
Файл 1.cpp | Файл 2.cpp |
#include "stdafx.h" #include <iostream> //1 int fst = 1; //2 int snd = 3; //3 void printFst(); //4 void printSnd(); //5 void main(){ //6 std::cout<<"\n fst = "<<fst;//7 printFst(); //8 printSnd(); //9 getchar(); } //10 | #include "stdafx.h" #include <iostream> //11 int fst = 2; //12 extern int snd; //13 void printFst(){ //14 std::cout<<"\n fst = "<<fst;//15 } //16 void printSnd(){ //17 std::cout<<"\n snd = "<<snd;//18 } //19 |
Результаты работы программы:
fst = 1
fst = 2
snd = 3
Текст программы размещён в двух файлах (модулях) 1.срр и 2.срр. В файле 1.срр в строках 2 и 3 определены две глобальные переменные fst и snd с внешним типом компоновки, которая даёт возможность доступа к этим переменным из другого модуля. Для того чтобы реализовать эту возможность, необходимо описать переменные в другом модуле так, как показано на примере переменной snd в строке 13 модуля 2.срр. В строке 12 модуля 2.срр определена глобальная переменная fst с внутренним типом компоновки, которая может быть доступна только в модуле 2.срр и никак не связана с переменной fst из строки 2. Таким образом, в программе определены три глобальные переменные, причём snd доступна в обоих модулях, а переменные fst только в том модуле, где они определены.
Сказанное подтверждается результатами выполнения программы. В функции main() сначала печатается переменная fst, определенная в модуле 1.срр, затем вызываются функции printFst() и printSnd(), определение которых размещено в модуле 2.срр. Функция printFst() выводит на экран значение переменной fst из второго модуля, а не из первого. Функция printSnd() выводит на экран значение переменной snd, которое стало доступно в нем благодаря описанию в строке 13.
Описание переменной во многом схоже по своей сути с описанием функции. Описаний одной и той же переменной в тексте программы может быть много, а определение должно быть только одно. Описание сообщает компилятору, что где-то в тексте программы есть определение переменной с таким-то именем и такого-то типа. Определение переменной отличается от описаний наличием в описании ключевого слова extern или наличием в определении явной инициализации переменной.
Необходимо помнить, что в программе нельзя определять переменные с одним именем, у которых совпадали бы тип, область существования, сфера действия, видимость и тип компоновки.
Всё вышесказанное относится не только к переменным основных типов данных, но и ко многим объектам (массивам, указателям, ссылкам, объектам типов, определенных пользователем и т.д.) языка Си++. Поэтому понимание этого материала особенно важно для дальнейшего изучения языка. Ниже дана таблица способов определения объектов и их свойств
Таблица 3.1
Объекты и их свойства
Способ определения объекта | Вид объекта | Свойства объекта |
Определен вне блока | Глобальный | Статическая продолжительность существования. Область видимости - вся программа. Возможен либо внутренний, либо внешний тип компоновки. Класс памяти extern либо static |
Определен в блоке со спецификатором static | Локальный статический | Статическая продолжительность существования. Область видимости с момента определения до конца блока. Возможен только внутренний тип компоновки. Класс памяти static |
Определен в блоке, в списке формальных параметров функции | Локальный автоматический | Локальная продолжительность существования. Область видимости с момента определения до конца блока. Возможен только внутренний тип компоновки. Класс памяти auto либо register |
Определен с помощью функции void*malloc(int) или оператора new. Уничтожен с помощью функции free(void*) или оператора delete | Объект с динамической продолжительностью существования | Существует с момента создания с помощью вызова функции malloc() или оператора new и до момента уничтожения с помощью вызова функции free() или оператора delete. Объект определяется внутри блока (тела функции), но при выходе за пределы блока не уничтожается так, как локальный статический объект. Программист должен обеспечить видимость объекта за пределами блока для того, чтобы, когда объект перестанет быть нужен, иметь возможность его уничтожить. |
В таблице обобщён материал об определении и описании объектов и их свойств. Кроме того, в ней кратко дано описание объекта с динамической продолжительностью существования. Такие объекты подробно будут рассмотрены далее при изучении указателей.
Дата добавления: 2020-12-11; просмотров: 459;