Скачиваний:
2
Добавлен:
04.09.2023
Размер:
1.17 Mб
Скачать

15 Классы памяти

Способы организации оперативной памяти (память автоматическая, динамическая, статическая, глобальная).

Автоматическая память

Все переменные в программе характеризуются не только типом, но и классом памяти. В языке Си существует четыре класса памяти: автоматический (automatic), регистровый(register), статический(static) и внешний(external).

Автоматические переменные в программе можно описать так:

auto A; auto char c1; auto int x= 125;

Описатель auto используется по умолчанию. Зона действия автоматической переменной ограничена блоком или функцией, где она описана. Она начинает существовать после обращения к функции и исчезает после выхода из нее. Таким образом автоматические переменные не занимают область в памяти. Значение автоматической переменной не может быть изменено другими функциями и в этих функциях могут находиться переменные с таким же именем.

Внешняя память

Внешние переменные вводятся как нечто противоположное автоматическим. Это глобальные переменные и к ним можно обращаться именами из любой функции. Поскольку внешние переменные доступны везде, их можно использовать для связи между функциями, не пренебрегая механизму формальных параметров.

Внешние переменные могут определяться вне какой-либо функции; при этом выделяется фактическая память. В любой другой функции, обращающейся к этим переменным, они должны описываться; делается явно с помощью описателя extern. Все внешние переменные размещают в начале исходного модуля (вне всяких функций!), опуская дополнительные описания со словом extern внутри функций. Конечно, если внешняя переменная и функция, которая ее использует, размещены в разных файлах, описывать эту переменную в функции необходимо. Но самым важным способом является описание каждой внешней переменной с ключевого слова extern в любой функции, которая ее использует. А еще лучше избегать применения внешних переменных, так как они часто служат источником трудно обнаруживаемых ошибок.

int x=3; /*описание внешней переменной */

/* увеличение x */

int plus1()

{

x=x+1;

printf("прибавляем единицу: x=%d\n",x);

}

/*уменьшение x */

int mainus1()

{

x=x-1;

printf("вычитаем единицу: x=%d\n",x);

}

main()

{

printf("начальное значение x=%d\n",x);

plus1();

minus1();

minus1();

printf("конечное значение x=%d \n", x);

}

Статическая память

Статические переменные, подобно автоматическим, локальны в той функции или блоке, где они описаны. Разница заключается в том, что статические переменные не исчезают, когда функция (блок) завершает работу, и их значения сохраняются для последующих вызовов функции. Описание статических переменных выглядит так:

static char c; static int a=1;

*статические переменные*/

#include <stdio.h>

plus1()

{

static int x=0;

x=x+1;

printf("x=%d\n",x);

}

main()

{

plus1();

plus1();

plus1();

}

Динамическая память

Регистровые переменные объявляются в программе с помощью ключевого слова register, и по замыслу автора языка Си должны храниться в сверхбыстрой памяти ЭВМ - регистрах. Используются аналогично автоматическим переменным. Целесообразность их применения для увеличения быстродействия программы представляется в большинстве случаев сомнительной.

Спецификаторы позволяют определить класс памяти определяемого объекта:

• auto. Этот спецификатор автоматического класса памяти указывает на то, что объект располагается в локальной (или автоматически распределяемой) памяти. Он используется в операторах объявления в теле функций, а также внутри блоков операторов. Объекты, имена которых объявляются со спецификатором auto, размещаются в локальной памяти непосредственно перед началом выполнения функции или блока операторов. При выходе из блока или при возвращении из функции (о механизмах вызова функций и возвращения из них речь ещё впереди), соответствующая область локальной памяти освобождается и все ранее размещённые в ней объекты уничтожаются. Таким образом спецификатор влияет на время жизни объекта (это время локально). Спецификатор auto используется редко, поскольку все объекты, определяемые непосредственно в теле функции или в блоке операторов и так по умолчанию располагаются в локальной памяти. Вне блоков и функций этот спецификатор не используется.

• register. Ещё один спецификатор автоматического класса памяти. Применяется к объектам, по умолчанию располагаемым в локальной памяти. Представляет из себя "ненавязчивую просьбу" к транслятору (если это возможно) о размещении значений объектов, объявленных со спецификатором register в одном из доступных регистров, а не в локальной памяти. Если по какой-либо причине в момент начала выполнения кода в данном блоке операторов регистры оказываются занятыми, транслятор обеспечивает с этими объектами обращение, как с объектами класса auto. Очевидно, что в этом случае объект располагается в локальной области памяти.

• static. Спецификатор внутреннего статического класса памяти. Применяется только(!) к именам объектов и функций. В C++ этот спецификатор имеет два значения. Первое означает, что определяемый объект располагается по фиксированному адресу. Тем самым обеспечивается существование объекта с момента его определения до конца выполнения программы. Второе значение означает локальность. Объявленный со спецификатором static локален в одном программном модуле (то есть, недоступен из других модулей многомодульной программы) или в классе (о классах - позже). Может использоваться в объявлениях вне блоков и функций. Также используется в объявлениях, расположенных в теле функций и в блоках операторов.

• extern. Спецификатор внешнего статического класса памяти. Обеспечивает существование объекта с момента его определения до конца выполнения программы. Объект, объявленный со спецификатором extern доступен во всех модулях программы, то есть глобален.

Выбор класса памяти, помимо явных спецификаторов, зависит от размещения определения или объявления в тексте программы. Модуль, функция, блок могут включать соответствующие операторы объявления или определения, причём всякий раз определяемый объект будет размещаться в строго определённых областях памяти.

Область видимости и время жизни переменных

Каждая переменная доступна в рамках определенного контекста или области видимость. Вне этого контекста переменная уже не существует.

Существуют различные контексты:

  • Контекст класса. Переменные, определенные на уровне класса, доступны в любом методе этого класса. Их еще называют глобальными переменными или полями

  • Контекст метода. Переменные, определенные на уровне метода, являются локальными и доступны только в рамках данного метода. В других методах они недоступны

  • Контекст блока кода. Переменные, определенные на уровне блока кода, также являются локальными и доступны только в рамках данного блока. Вне своего блока кода они не доступны.

Например, пусть класс Program определен следующим образом:

Person tom = new();

tom.PrintName();

tom.PrintSurname();

 

class Person                            // начало контекста класса

{

    string type = "Person";             // переменная уровня класса

    public void PrintName()             // начало контекста метода PrintName

    {

        string name = "Tom";            // переменная уровня метода

 

        {                               // начало контекста блока кода

            string shortName = "Tomas"; // переменная уровня блока кода

            Console.WriteLine(type);    // в блоке доступна переменная класса

            Console.WriteLine(name);    // в блоке доступна переменная окружающего метода

            Console.WriteLine(shortName);// в блоке доступна переменная этого же блока

        }                               // конец контекста блока кода, переменная shortName уничтожается

 

        Console.WriteLine(type);        // в методе доступна переменная класса

        Console.WriteLine(name);        // в методе доступна переменная этого же метода

        //Console.WriteLine(shortName); //так нельзя, переменная c определена в блоке кода

        //Console.WriteLine(surname);     //так нельзя, переменная surname определена в другом методе

 

    }       // конец контекста метода PrintName, переменная name уничтожается

 

    public void PrintSurname()      // начало контекста метода PrintSurname

    {

        string surname = "Smith";   // переменная уровня метода

 

        Console.WriteLine(type);        // в методе доступна переменная класса

        Console.WriteLine(surname);     // в методе доступна переменная этого же метода

    }       // конец конекста метода PrintSurname, переменная surname уничтожается

 

}   // конец контекста класса, переменная type уничтожается

Здесь определенно четыре переменных: type, name, shortName и surname. Каждая из них существует в своем контексте. Переменная type существует в контексте всего класса Person и доступна в любом месте и блоке кода в методах PrintName и PrintSurname.

Переменная name существует только в рамках метода PrintName. Также как и переменная surname существует в рамках метода PrintSurname. В методе PrintName мы не можем обратиться к переменной surname, так как она в другом контексте.

Переменная shortName существует только в блоке кода, границами которого являются открывающая и закрывающая фигурные скобки. Вне его границ переменная shortName не существует и к ней нельзя обратиться.

Нередко границы различных контекстов можно ассоциировать с открывающимися и закрывающимися фигурными скобками, как в данном случае, которые задают пределы блока кода, метода, класса.

При работе с переменными надо учитывать, что локальные переменные, определенные в методе или в блоке кода, скрывают переменные уровня класса, если их имена совпадают:

class Person

{

    string name = "Tom";             // переменная уровня класса

    public void PrintName()

    {

        string name = "Tomas";      // переменная уровня метода скрывает переменную уровня класса

 

        Console.WriteLine(name);    // Tomas

    }

}

При объявлении переменных также надо учитывать, что в одном контексте нельзя определить несколько переменных с одним и тем же именем.