Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект С++ (Часть 2).doc
Скачиваний:
16
Добавлен:
10.09.2019
Размер:
816.64 Кб
Скачать

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

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

[< класс памяти >] < тип данных > < идентификатор > [< инициализация >]

В языке C++ определены 4 основные класса памяти:

  • auto

  • register

  • static

  • extern

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

void F ( int i )

{

int j = 20;

if ( i == 10 )

{

double Pi = 3.14;

……..

}

}

В этой функции все переменные (i, j, Pi) являются локальными автоматическими объектами. Переменные i и j создаются в памяти в начале выполнения функции и уничтожаются при выходе из функции. Время жизни и область видимости этих переменных одинаковы и соответствуют всему телу функции. Переменная Pi создается в памяти, когда начинает выполняться соответствующий блок инструкции if, и уничтожаются при выходе из этого блока.

Спецификатор register используется довольно часто для увеличения быстродействия программы. Этот спецификатор “просит” компилятор поместить соответствующую переменную в памяти так, чтобы доступ к ней осуществлялся как можно быстрее. Регистровые переменные размещаются либо в регистрах процессора, либо в кэш-памяти компьютера. Поскольку объем такой памяти ограничен, компилятор эту “просьбу” может и не выполнить. В этом случае регистровые переменные создаются как обычные автоматические переменные. Пример определения регистровой переменной:

register double d = 1.23;

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

void F ()

{

extern int ArrSize;

……..

}

………

int ArrSize = 100;

………

Когда компилятор встречает спецификатор extern, он не создает в памяти новую переменную, а пытается найти переменную ArrSize далее в этом файле, а затем в других файлах проекта. Если такая глобальная переменная находится, то она и используется в функции F. Говорят, что строка extern int ArrSize; является объявлением переменной (переменная только объявляется, но не создается в памяти), а строка int ArrSize = 100; - определением переменной, при котором создается сама переменная.

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

  • локальные static-переменные, определяемые внутри функций;

  • внешние static-переменные, определяемые вне функций.

Локальные static-переменные определяются внутри функций и после своего определения существуют до конца работы программы, сохраняя свои значения. После завершения функции они не уничтожаются и сохраняют свои значения до следующего вызова этой функции. Область видимости таких переменных ограничена только блоком функции, в которой они определены, и в других функциях они не видны. Хорошей иллюстрацией использования локальных статических переменных является функция для получения текущего среднего значения числовой последовательности, вычисляемого в процессе ввода элементов этой последовательности:

double Srednee ( int n );

int main ()

{

setlocale (0, "");

int n;

do

{

cin >> n;

if ( n >= 0 )

cout << "Среднее значение: " << Srednee ( n );

cout << endl;

}

while ( n >= 0 );

return 0;

}

double Srednee ( int n )

{

static int Sum = 0, Count = 0;

++ Count;

Sum += n;

return double ( Sum ) / Count;

}

Локальные static-переменные Sum и Count инициализируются значениями 0 один раз при первом вызове функции Srednee. Затем их значения изменяются, и эти измененные значения после завершения работы функции сохраняются до ее следующего вызова.

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

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

Спецификатор static можно применять и к функциям.

Многофайловые проекты

Напишу позже!!!

10. Структуры, объединения, перечисления

Все данные, с которыми работает программа можно разделить на две группы:

  • простые;

  • структурированные.

Из рассмотренных ранее типов данных к структурированным типам относятся массивы, остальные типы данных – простые – не имеющие внутренней структуры.

Структуры и объединения относятся к структурированным (конгломератным) типам данных, определяемых программистом.

10.1. Структуры

Определение структур

Структуры представляют собой группы связанных между собой, как правило, разнотипных переменных, объединенных в единый объект.

Для работы с некоторой структурой в программе необходимо:

  1. описать тип данных этой структуры;

  2. определить переменные этого типа для хранения соответствующих данных в памяти.

Рассмотрим пример: сведения о студенте содержат следующие данные:

  • фамилия - Fam;

  • имя - Name;

  • год рождения - Year;

  • пол - Sex;

  • средний балл - Grade.

Представим все эти данные в виде единой структуры. Введем новый тип данных (назовем его t_Student) для описания этой структуры:

struct t_Student {

char Fam [20],

Name [16];

short Year;

bool Sex;

float Grade;

};

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

Вся эта конструкция является инструкцией языка программирования, поэтому после нее всегда должен ставиться символ ;.

При описании структуры память для размещения данных не выделяется. Работать с описанной структурой можно только после того, как будет определена переменная (переменные) этого типа данных, только при этом компилятор выделит необходимую память:

t_Student St1, St2; // Определены две переменные типа t_Student

Определение переменных можно осуществить одновременно с описанием типа данных структуры:

struct t_Student {

char Fam [20],

Name [16];

short Year;

bool Sex;

float Grade;

} St1, St2;

Все поля структурных переменных располагаются в непрерывной области памяти одно за другим. Общий объем памяти, занимаемый структурой, равен сумме размеров всех полей структуры. Для определения размера структуры следует использовать инструкцию sizeof (): sizeof (t_Student) или sizeof (St2).