ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 22.12.2021
Просмотров: 191
Скачиваний: 2
КЛАСИ ПАМ'ЯТІ
1. КЛАСИ ПАМ'ЯТІ ТА ВИДИ ДІЙ ІМЕН ЗМІННИХ. ОБЛАСТЬ ВИДИМОСТІ ТА ЧАС ЖИТТЯ
Кожна змінна і функція, описана в програмі мовою C++, належить до якогось класу пам'яті. Клас пам'яті змінної визначає час її існування (час життя об'єкта), область видимості й місце, де об'єкт розташовується (внутрішні регістри процесора, сегмент даних, сегмент стека). Область видимості – це область початкового коду програми, з якого можливий коректний доступ до пам'яті або функції з використанням ідентифікатора. Час життя визначає час існування змінної в процесі виконання програми. З погляду часу життя об'єкта розрізнюють три типи об'єктів: статичні, локальні та динамічні.
Об'єкти зі статичною тривалістю життя отримують розподіл пам'яті на початку виконання програми – такий розподіл пам'яті зберігається до виходу з програми, розміщується в сегменті даних.
Об'єкти з локальною тривалістю життя створюються при вході в функцію, а при виході з функції знищуються, розміщуються в стеку чи регістрах.
Об'єкти з динамічною тривалістю життя створюються і знищуються спеціальними функціями керування пам'яттю, розміщуються в купі.
Існують чотири області видимості змінних: блок, функція, прототип функції і файл. Змінні, оголошені всередині блоку, мають областю дії блок. Блок починається оголошенням змінних і закінчується кінцевою правою фігурною дужкою блоку. Якщо блоки вкладені і змінна у зовнішньому блоці має таке саме ім'я, як змінна у внутрішньому блоці, змінна зовнішнього блоку невидима до моменту завершення роботи внутрішнього блоку. Змінні, оголошені на початку функції, областю видимості мають блок, який повністю містить тіло функції. Змінні, оголошені всередині прототипу функції, мають спеціальну область видимості, яка простягається від точки оголошення до кінця прототипу функції. Змінні, оголошені поза будь-якою функцією, мають областю дії файл.
Можливість роботи з різними класами пам'яті в процесі програмування дає програмісту механізм створення гнучких з погляду використання ОЗП програм. Клас пам'яті, що призначається змінній, визначає не лише тривалість збереження її значень, а й також спосіб інформаційного зв'язку з іншими функціями, які теж працюють з цією змінною. У мові C++ розрізняють чотири класи пам'яті, які означаються такими ключовими словами:
• auto (автоматична пам'ять);
• extern ( зовнішня пам'ять);
• static (статична пам'ять);
• register (регістрова пам'ять).
Визначення класу пам'яті для змінної відбувається на етапі її оголошення в програмі. За замовчуванням, тобто якщо не задається програмістом, змінній призначається клас auto. Клас пам'яті для змінної задається або за розташуванням її опису, або за допомогою специфікаторів класу, перелічених вище. Специфікатори передують оголошенню змінної.
2. АВТОМАТИЧНІ ЗМІННІ
Автоматичні змінні, або ж auto, описуються всередині відповідної функції і ділянка їх дії лежить у межах даної функцій тобто змінна починає існування в момент активізації функції і зникає в момент завершення функції. Ще автоматичні змінні називають локальними. Спроба роботи з локальною змінною в інших функціях призводить до помилок. Оскільки за замовчуванням змінним призначається клас auto, то ключове слово auto можна не використовувати.
Два наведені нижче записи можна вважати еквівалентними.
float a, b;
auto float a, b;
Область видимості змінної автоматичного класу пам'яті починається з її визначення і завершується при виявленні кінця блоку, в якому ця змінна визначена. Доступ до таких змінних із зовнішнього блоку неможливий. Оскільки локальні змінні знищуються при виході з функції, в якій вони оголошені, то ці змінні не можуть зберігати значення між викликами функцій. Якщо не визначене місце для зберігання локальних змінних, то вони зберігаються в стеку. Пам'ять для автоматичних змінних відводиться динамічно під час виконання програми при вході в блок, у якому описана відповідна змінна. При виході з блоку пам'ять, відведена під усі його автоматичні змінні, автоматично звільняється. Звідси й походить термін автоматичні змінні. Доступ до автоматичних змінних можливий лише з блоку, де змінні описані, оскільки до моменту входу в блок змінна взагалі не існує і пам'ять під неї не виділена.
3. ГЛОБАЛЬНІ ЗМІННІ
Зовнішні змінні, або ж extern, описуються поза функцією, але можуть бути описані і всередині функції — обов'язково зі специфікатором extern. Ділянкою дії змінних є всі функції програмного комплексу, тому ці змінні інакше називають глобальними.
Якщо змінна визначена перед основною функцією програмного комплексу, то в інших функціях, що входять у програмний файл, її можна не описувати як зовнішню, вона й так діятиме в них. До них можна отримати доступ у будь-якому виразі, незалежно від того, в якій функції знаходиться даний вираз.
Опис змінної як extern усередині функції потрібний у тих випадках, коли цю змінну необхідно використати, а вона визначена або в функції, яка активізується пізніше, або в іншому програмному файлі. Наприклад,
funk ()
{ extern double pi;
….
}
double pi = 3.14159;
void main ()
{…
}
Функції main () і funk () розташовані в одному початковому файлі, але визначення зовнішньої змінної рі знаходиться після функції funk (), у цьому разі потрібне оголошення зовнішньої змінної рі в функції funk зі специфікатором extern. Оголошення зовнішніх змінних інформує компілятор, що така змінна вже існує і пам'ять для неї вже виділена.
Для того, щоб функції могли працювати з зовнішньою змінною, визначеною в іншому файлі, вони повинні також містити її оголошення. Наприклад,
// файл f1.cpp
double рі = 3.14159;
void main ( )
{…
}
// файл f2.cpp
funk ()
{ extern double pi;
…
}
Якщо всередині блоку описана автоматична змінна, ім'я якої збігається з ім'ям глобальної змінної, то всередині блоку глобальна змінна маскується під локальну. Це означає, що всередині даного локального блоку буде видна автоматична змінна. Для розв'язання цієї проблеми в C++ можна використати операцію дозволу області видимості ::. Унарна операція :: дозволяє отримати доступ до зовнішньої іменованої області пам'яті для певної функції. Наприклад,
#include <iostream.h>
int x; //Глобальна змінна
void main()
{ int x=1234; //Локальна змінна
::х=23; // Присвоїти значення глобальній змінній
cout << "\nЛокальна змінна х=" << х;
cout << "\nГлобальна змінна х=" <<: :х;
}
Вираз ::х вказує, що використовується змінна х зовнішньої області дії, а не локальна.
Для зовнішніх змінних пам'ять відводиться один раз і залишається зайнятою до закінчення виконання програми. Якщо користувач не вкаже початкового значення глобальних змінних, то їм буде присвоєне значення нуль. Глобальні змінні зберігаються у фіксованій області пам'яті, що встановлюється компілятором.
4. СТАТИЧНІ ЗМІННІ
Статичні змінні, або ж static, мають таку ж ділянку дії, як і автоматичні змінні, але вони не зникають, коли функція що їх містить, закінчить свою роботу. Компілятор зберігає її значення від одного виклику функції до іншого. Локальна статична змінна при завершенні функції, в якій вона оголошена, ні втрачає свого значення. Статична змінна залишається локальною для цієї функції. При черговому виклику функції вона зберігає своє колишнє значення. Наприклад,
#include<iostream.h>
void main ()
{ int t = 0, і;
int t (void);
for(i = 0; і < 3; i++)
{ t = st();
cout <<t; }
}
int st()
{ static int s = 0;
s = s++;
return(s);
}
Внаслідок роботи програми будуть виведені такі значення: 1, 2, 3. s ініціалізується 0 при першому виклику функції, при черговому виклику функції s не ініціалізується, вона зберігає попередне значення.
Статичні змінні можуть бути описані поза будь-якою функцією, тоді вони інтерпретуються як зовнішні змінні. Різниця між зовнішньою змінною та статичною зовнішньою змінною полягає в тому, що остання з них може бути використана тільки функціями того програмного файла, в головній функції якого вона визначена, тобто доступна у власному файлі й більше ніде.
В C++ статична змінна, яка не ініціалізована, встановлюється й нуль.
Час життя статичних змінних глобальний: починається після визначення змінної і триває до кінця програми. Область видимості статичних змінних залежатиме від того, є вони зовнішніми чи внутрішніми.
5. РЕГІСТРОВІ ЗМІННІ
Регістрові змінні, або ж register, запам'ятовуються не її ОЗП, як усі інші змінні, а в регістрах центрального процесора, де доступ до них і робота з ними виконуються набагато швидше, ніж у пам'яті. В іншому регістрові змінні аналогічні автоматичним змінним.
При виділенні регістрової пам'яті запит програміста може бути не задоволений, оскільки регістри в поточний момент часу можуть бути недоступні. У цьому разі регістрова змінна стає простою автоматичною змінною.
Змінні регістрового класу пам'яті мають такі самі область видимості та час життя, як і автоматичні змінні. Не можна застосовувати register до глобальних змінних.
У процесі написання програмних комплексів необхідно cполучати різні види змінних за використанням пам'яті. Найекономніше витрачається пам'ять при роботі з автоматичними змінними. Тому треба прагнути там, де можна, використовувати цей клас пам'яті, а інші застосовувати в тих випадках, коли це необхідно, а саме:
-
зовнішні змінні слід використовувати, коли потрібно організувати передачу великих обсягів даних між кількома функціями;
-
статичні змінні в тому разі, коли необхідно неодноразово звертатися до функції і в процесі кожного звертання використовувати результати попереднього звертання;
-
регістрові змінні використовувати з метою прискорення розрахунків.
У табл. 1 наведені область видимості та час життя для змінних різних класів пам'яті.
Таблиця 1. ОБЛАСТЬ ВИДИМОСТІ ТА ЧАС ЖИТТЯ ЗМІННИХ
6. ДИНАМІЧНИЙ РОЗПОДІЛ ПАМ'ЯТІ
У мові C++ програміст має можливість проводити розподіл пам'яті не лише на початку програми, а й у процесі її виконання в необхідному місці. Для цього слід указати розмір блоку пам'яті, що виділяється як аргумент бібліотечної функції malloc (). Прототип функції такий:
void *malloc(size_t size);
де size — необхідний розмір у байтах пам'яті, що виділяється, тип size_t визначений як беззнакове ціле.
Дана функція виділяє пам'ять і повергає вказівник типу void* на початок виділеної пам'яті, а якщо виділити пам'ять не вдається, то повертає NULL. Прототип функції знаходиться в заголовних файлах <stdlib.h> і <alloc.h>. Наприклад,
char *p;
р = (char*) malloc (100);
Виділяється область пам'яті в 100 байтів, адреса якої присвоюється змінній р. Оскільки malloc() повертає вказівник типу void*, то необхідно використати при присвоєнні перетворення одного типу вказівника на інший.
Якщо необхідно виділити пам'ять для масиву, то це можна зробити за допомогою функції calloc (), прототип якої є таким:
void *calloc (size_t n, size_t size);
де n – кількість необхідних елементів пам'яті, size – розмір у байтах одного елемента, а розмір виділеної пам'яті дорівнює величині n*size. Повертає вказівник на перший байт виділеної нам'яті, якщо ж пам'яті недостатньо, то повертається нульовий вказівник. Наприклад,
float *p;
р = (float*) calloc (100, sizeof (float));
Виділяється пам'ять для 100 елементів, кожний розміром у 6 байтів.
Аргументи функцій malloc() і calloc() повинні бути цілого типу. Функція calloc () додатково до виділення пам'яті здійснює обнулення всього виділеного блоку.
Для звільнення виділеного функціями malloc() і calloc() об'єму пам'яті використовується функція free (). Прототип функції є таким:
void free (void *ptr);
де ptr – вказівник на блок пам'яті, що звільняється.
Функція повертає пам'ять, на яку вказує параметр ptr, в купу. Для звільнення пам'яті з попереднього прикладу необхідно записати такий вираз:
free(p);
Оскільки пам'ять виділяється для певної мети і звільняється, коли її використання завершилося, то можна використати ту саму пам'ять в інший момент часу для інших цілей в іншій частині програми.
7. ОПЕРАЦІЇ NEW I DELETE
ДЛЯ ДИНАМІЧНОГО РОЗПОДІЛУ ПАМ'ЯТІ
Мова C++ виділяє дві операції для динамічного розподілу пам'яті – new і delete. Операції new і delete виконують динамічний розподіл і скасування розподілу пам‘яті, але з більш високим пріоритетом, ніж стандартні бібліотечні функції malloc і free. Формат їхній є таким:
<змінна покажчик> = new <тип змінної> (<ініціалізатор>);
delete(<змінна покажчик >);
де змінна покажчик — змінна типу покажчик; ініціалізатор – вираз у круглих дужках.
Операція new дозволяє виділити і зробити доступною ділянку в основній пам'яті, розміри якої відповідають типу даних, що визначається ім'ям типу. У виділену ділянку заноситься значення, що визначається ініціалізатором, який не є обов'язковим елементом. У разі успішного виконання операції new повертає адресу початку виділеної ділянки пам'яті. Якщо ділянка потрібних розмірів не може бути виділена, то операція new повертає нульовий покажчик (NULL).
Покажчик, якому присвоюється значення адреси, що отримується, має відноситися до того самого типу даних, що й <тип змінної> в операції new. Наприклад, new float;
new int(15);
У першому випадку операція new виділяє ділянку пам'яті розміром 4 байти, у другому — ділянку пам'яті в 2 байти й ініціалізує цю ділянку значенням 15.
int *h; // визначення покажчика
h = new int(2); // виділення пам'яті для int і ініціалізація 2
……….
delete h; // звільнення пам'яті
Покажчик h пов'язаний з ділянкою пам'яті, виділеною для величини цілого типу. Надалі доступ до виділеної ділянки пам'яті забезпечує вираз *h.
При виділенні пам'яті для масиву запис операції буде таким:
<змінна покажчик > = new <тип змінної> [<розмірність>];
Повертаний new покажчик, вказує на перший елемент масиву. Наприклад,
int *mat_ptr = new int[3][10];
Буде виділена ділянка динамічної пам'яті розміром 3*10*4 байти.
При виділенні динамічної пам'яті для масиву його розміри мають бути повністю визначені. Тільки перший (найлівіший) розмір масиву може бути заданий з допомогою змінної, інші розміри багатомірного масиву можуть бути визначені лише за допомогою констант.
Тривалість існування виділеної за допомогою операції new ділянки пам'яті — від точки її створення до кінця програми або до явного її звільнення за допомогою операції delete.
delete <змінна покажчик >;
де покажчик адресує ділянку пам'яті, що звільняється, раніше виділену за допомогою операції new. Повторне застосування delete до того самого покажчика дає невизначений результат.
Для звільнення динамічно розміщеного масиву необхідно використати таку форму запису delete:
delete[ ] <змінна покажчик >;
Квадратні дужки повідомляють про те, що необхідно звільнити пам'ять, виділену для масиву. Наприклад,
delete[ ] matr_ptr;
Приклад. Виділяється пам'ять для 10 елементів масиву типу float. Елементам масиву присвоюються значення від 100 до 109. Вміст масиву виводиться на екран.
#include <iostream.h>
void main()
{
float *p; int i;
p = new float[ 10];
for (i = 0; і < 10; i++)
*(p + i)= 100 + i;
for (i = 0; і < 10; i++)
cout << *(p+i);
delete [ ] p;
}
Операція new має ряд переваг перед функцією malloc().
-
По-перше, new автоматично обчислює розмір пам'яті, немає необхідності використати операцію sizeof.
-
По-друге, операція new автоматично повертає покажчик необхідного типу, немає необхідності використовувати операцію перетворення типу.
-
По-третє, є можливість ініціалізації об'єкта при використанні операції new.