1 Классы памяти Переменная-это именованная область памяти. Имя переменной –это ссылка на некоторую область памяти. Переменная - частный случай леводопустимого выражения Леводопустимое выражение – это конструкция для обращения к некоторому участку памяти, куда можно поместить значение (l – value, l-значение). Понятие леводопустимого выражения включает все возможные формы обращения к некоторому участку памяти с целью изменения его содержимого. Леводопустимые выражения: 1) имена скалярных переменных 2) имена элементов массивов 3) имена указателей 4) ссылки на l-value (синонимы l-value) 5) имена элементов структурированных данных имя структуры . имя элемента указатель на структуру -> имя элемента элемента 6) выражения с операцией ‘*’ int i , * p =&i; *p=7; *&i=2; 7)вызовы функций возвращающих ссылки на объекты программы Примеры праводопустимых выражений: 1) любое арифметическое, логическое и т. д. выражение 2) имя константы 3) имя функции (указатель константа) 4) имя массива (указатель константа) 5) вызов функции, не возвращающей ссылки 2 В дальнейшем рассмотрении, в качестве l-value будет рассматриваться переменная. Переменные характеризуются типами, а также классами памяти. Класс памяти Класс памяти определяет: 1) место размещения переменной (ОП или регистры) 2) область действия имени (идентификатора), связанного с переменной 3) видимость объекта 4) время существования 5) тип компоновки (связывания) Явно задать класс памяти можно с помощью спецификаторов: auto, register, static, extern. Место размещения Ниже указаны спецификаторы класса памяти и соответствующее им место размещения объекта: auto - автоматическая, локальная оперативная память (стек - временная память).Спецификатор auto может быть задан при определении переменной блока, например в теле функции. Этим объектам память выделяется при входе в блок и освобождается при выходе из него. Вне блока переменные класса auto не существуют. register - автоматическая регистровая память (регистры процессора). Спецификатор register аналогичен auto, но для размещения переменной используется не оперативная память, а регистры процессора. Если регистры заняты другими переменными, переменные класса register обрабатываются как объекты класса auto. 3 static - статическая (сегмент данных). Объект этого класса будет существовать в пределах того файла программы, где он определен. Этот класс может быть приписан как переменным, так и функциям. extern - внешняя, глобальная память (сегмент данных). Объект класса extern глобален, то есть, доступен во всех файлах программы. Этот класс может быть приписан как переменным, так и функциям. Класс памяти переменной определяется 1) по умолчанию размещением определения переменной в программе; 2) синтаксисом определения переменной - наличием спецификаторов, присутствующих в определении переменной. Область действия (ОД) имени ОД – это часть программы, в которой можно обращаться к данному имени (сфера действия имени в программе). Рассмотрим все случаи: 1) Имя определено в блоке (локальные переменные): точки определения до конца блока; ОД - от 2) Формальные параметры в определении функции (форм. пар. это локальные переменные функции) : ОД параметров – блок тела функции; 3) Метки операторов: ОД – функция; 4) Глобальные объекты: ОД - вся программа от точки их определения или от точки их описания; 4 5) Формальные параметры в прототипе функции: ОД- прототип. Область видимости (ОВ) Понятие области видимости понадобилось в связи с возможностью повторных определений идентификатора внутри вложенных блоков или функций. В этом случае разрывается связь имени переменной с участком памяти, хотя сфера действия имени сохраняется. ОВ – это часть программы, в которой реально обращение к имени переменной позволяет обратиться к данному участку памяти. ОВ может быть меньше ОД, если внутри блока, где определена переменная, имеется внутренний блок, в котором то же имя дано другой переменной (то есть, связано с другим участком памяти). Тогда во внутреннем блоке внешняя переменная теряет видимость. ОД >= ОВ int k Пример1: #include<iostream.h> int k=0; void main() { int k=1; { cout<< k; char k = 'A'; cout<< k; cout<< ::k; cout << k ; } cout << k ; } результат : 1A0A1 char k 5 Пример2: int a; // определена глобальная переменная а void main() { extern int a, b; …} //описаны две глобальных переменных, //причем одна еще не определена, но использовать b после //описания можно void func1 ( ) {extern int b; …} // описана глобальная переменная b до //определения и ее можно использовать в теле функции, //доступна также переменная a void func2 ( ) {…} // a – доступна , b – не доступна, т.к. не было еще ее //определения int b; // определение глобальной переменной b void func3( ) { int a; … } // глобальная a потеряла видимость, т.к. объявлена //локальная переменная с тем же именем, глобальная b - видна void func4 ( ) {int b; …} // видна глобальная переменная a // , а глобальная b - потеряла свою видимость, т.к. объявлена //локальная переменная b с тем же именем. Продолжительность существования Продолжительность существования - это время, в течение которого, идентификаторы соответствуют участкам памяти. Бывает статическая, локальная и динамическая продолжительность. При статической продолжительности память выделяется при определении объекта и сохраняется до конца программы. 1) По - умолчанию все глобальные переменные (т.е. объявленные вне всех функций) обладают статической продолжительностью. 2) Локальные переменные (объявленные в функциях), со спецификатором statiс также имеют статическую продолжительность. 6 3) Статическая переменная, локализованная в функции, не теряет своего значения при отработке функции. Инициализируется статическая переменная только при первом вызове функции, при втором вызове и т. д. – начальное значение этой переменной будет то, что сохранилось при предыдущем вызове. 4)Глобальные и статические переменные по умолчанию инициализируются нулями. 5) Статическую продолжительность имеют все функции. 6) Если в функции имеется описание переменной со спецификатором extern, это означает, что в функции представлено описание некоторой внешней переменной, определение которой дано в другом месте. Такая переменная имеет статическую продолжительность существования. Пример: … int V ( void) { static int k; //статическая переменная инициализирована 0 return ++k; } void main () { int k=0; // другая локальная переменная for( ; k <=3 ; k++) cout<< “\n”<<V ( ) ; } 1 2 3 4 Локальной продолжительностью существования обладают автоматические (локальные) переменные, объявленные в блоке. При входе в блок, переменной выделяется место на регистрах процессора, или в оперативной памяти - в стеке и память освобождается при завершении блока. Должна быть явная инициализация локальных переменных, иначе их начальное значение не предсказуемо. 7 Отметим еще раз, если локальная переменная, объявленная в функции (т.е. локальная область действия), но со спецификатором static, эта переменная имеет статическую продолжительность существования - до конца программы. Объекты с динамической продолжительность создаются и уничтожаются с помощью операторов в процессе выполнения программы по желанию программиста. Память таким переменным выделяется в динамической области памяти, называемой кучей. Продолжительность от точки объявления до момента программного освобождения памяти или до конца программы, если память программно не была освобождена. Для выделения памяти используются операция new и функция malloc(), прототип которой находится в файле alloc.h: void* malloc( int size); size - размер в байтах, выделяемой памяти. Функция возвращает значение указателя без типа, поэтому при выделении памяти надо производить приведения типа. Освобождение памяти происходит с помощью операции delete и функции free (). … void main( ) { int*m = (int*) malloc (sizeof(int)); //использование функции // для выделения памяти на одну переменную типа int free(m); //освобождение памяти // аналогично используя операцию: int*r = new int ; delete r; // использование операций } Тип компоновки Если программа состоит из нескольких файлов, каждому имени, используемому в нескольких файлах, может соответствовать следующие ситуации: 1) один объект, общий для всех файлов, таким объектам компоновщик обеспечивает внешнее связывание; 8 2) один и более объектов в каждом файле, т.е. объекты, локализованы в файлах, для них используется внутреннее связывание; Тип компоновки компилятор устанавливает по контексту. Определения и описания Все описанные выше атрибуты (тип, класс, ОД и т. д.) приписываются объекту при его определении (объявлении) или при его описании. В чем разница между определением или описанием. При определении (definition) или объявлении объекта ему дается имя, и устанавливаются атрибуты объекта, в соответствии с которыми выделяется нужный объем памяти и определяется формат внутреннего представления объекта. При определении происходит связывание имени объекта с участком памяти. Определение выполняет инициализацию объекта. Определение объекта может быть только одно в программе! Описание или декларация (declaration) дает знать компилятору, что объект определен и напоминает свойства объекта (в основном компилятор отслеживает типы!). Таким образом, описание - это представление в конкретной функции уже объявленного где – то объекта. Описаний объекта может быть несколько! Нередко описание и определение по внешнему виду совпадают. Не совпадают они в следующих случаях: 1)описание – прототип функции 2)описание содержит спецификатор extern 3)описывается класс или структурный тип 4)описывается статический компонент класса 5)описывается имя типа с помощью typedef 9 Определения char c; Описания extern int g; -//переменная //внешняя const float pi=3.14; extern const float pi- //внешняя // конст. float Func (float x) { return x*x} float Func (float x) ; //// прототип struct {char a; int b;}st // // структура float d = 1.0- инициализация struct st ; //– имя структуры // структуры без полей typedef char symbol; Определения(объявление) переменных S m тип имя1 иниц.1, имя2 иниц.2, … ; S , m , иниц. -не обязательны S -спецификаторы классов памяти auto , static , extern , register m –модификатор: const - указывает на постоянство объекта valatile – указывает на изменчивость непосредственного обращения к нему. объекта без Инициализация проводится двумя способами: имя = инициализирующее выражение; имя (инициализирующее выражение) – применяется только в функциях Имя переменных: 1) Две глобальных переменных не могут иметь одинаковые имена. 2) Локальная переменная может иметь одинаковое имя с глобальной переменной и тогда в данном блоке становится недоступна глобальная переменная с этим именем. 10 3) Локальные переменные разных блоков могут иметь одинаковые имена, т.к. у них различные ОВ. 4) Локальные переменные одного блока должны иметь различные имена, и если этот блок – тело некоторой функции, то и имена формальных параметров должны быть различными и не совпадать с именами локальных переменных функции.