- •Int main() /* головна обов’язкова функція, де ключове слово int означає, що функція main() повертатиме у точку виклику результат цілого типу*/ 6
- •Засоби розробки с програм
- •Директиви препроцесора.
- •Using namespace std; // вказуємо, що будемо працювати з іменами із стандартної бібліотеки
- •Запитання
- •Тема 2. Данні
- •Команда присвоєння. Правила узгодження типів.
- •Запитання
- •Тема 3. Операції та вирази
- •Тема 4. Потоки. Введення - виведення даних
- •Форматування даних
- •Введення-виведенні засобами бібліотеки сstdіo
- •Тема 5. Адреси даних. Вказівники. Динамічна пам’ять
- •Тема 6. Файли
- •Тема 7. Розгалуження
- •Логічні вирази та логічні операції.
- •Команда ?
- •Тема 8. Цикли
- •Команда циклу з післяумовою do-while має вигляд
- •Тема 9. Функції (1)
- •Тема 10. Функції (2)
- •Тема 11. Масиви
- •Тема 12. Рядки
- •Тема 13. Структури, списки, стеки, черги, об’єднання
- •Тема 14. Виняткові ситуації
- •Http://www.Rate1.Com.Ua/strannosti/2633/ Самі фатальні помилки в програмному забезпеченні
- •Помилки під час виконання (виняткові ситуації, виключення) Exceptions - це помилки, що виникають у випадку, коли програма намагається виконати неприпустиму операцію.
- •Тема 15. Графіка
- •Написание простых графических программ
- •Http://www.Rsdn.Ru/article/opengl/ogltut2.Xml Графическая библиотека OpenGl
Тема 5. Адреси даних. Вказівники. Динамічна пам’ять
Адреси даних (посилання, reference). Для розв’язування специфічних для мови C++ задач (робота з масивами, побудова графічних зображень і динамічних ефектів) потрібно знати не тільки значення деякої змінної, але й її адресу в оперативній пам’яті. Для визначення адреси даного у пам’яті є операція визначення адреси
&<назва даного>
Приклад 1. Розглянемо фрагмент програми
int а = 25;
cout << "Значення змінної а = "<<а<<"\n";
cout<<"Адреса змінної а "<<&а;
У результаті виконання цих команд на екрані одержимо:
Значення змінної а = 25
Адреса змінної а 0xaf72254
Адреси зображаються шістнадцятковими числами і під час кожного виконання програми можуть бути різними.
Вказівники. У мові C++ є ще один засіб визначення адреси даного - це вказівники. Вказівник - це змінна, значенням якої є адреса. Вона вказує на початок області оперативної пам’яті, де зберігається дане. Вказівники дають змогу оперувати не з іменами даних, а з їх адресами. Вказівники утворюють так:
<тип даного> *<назва вказівника;
Можна створювати вказівники на сталі, змінні, функції, інші вказівники тощо. Особливо ефективною є робота з вказівниками на рядки та масиви.
Приклад 1. int *nomer; float *rist, *prtA, *prtB;
Тут оголошено вказівник на цілий тип nomer і вказівники на дійсний тип rist, prtA, prtB. Надати значення вказівникам можна так:
<назва вказівника> = <адреса змінної>;
Приклад 2. Нехай у програмі оголошені змінні int n = 10; float studl, stud2, stud3;
Tоді можна оголосити вказівники nomer і rist на ці змінні:
nomer = &n;
rist = &stud1;
rist = &stud3;
Отримати вмістиме комірки, на яку вказує вказівник, можна за допомогою конструкції *<вказівник>, наприклад *rist є значенням змінної stud3.
Вказівники застосовують також для надання значень змінним:
rist = &stud1;
*rist = 1.65; // У комірку пам'яті для змінної stud1 буде занесено число 1.65
Тут вказівник rist вказуватиме на адресу змінної stud1, а власне змінній stud1 буде присвоєно значення 1.65.
Приклад 3. Обчислити довжини (в байтах) вказівників на різні типи даних можна так:
int *prt_i; double *prt_d; char *prt_c;
cout << "\nHa цілий тип " << sizeof(prt_i);
cout <<"\nHa дійсний тип " << sizeof(prt_d);
cout <<"\nНа символьний тип “<< sizeof(prt_c);
Переконайтесь, що усі вище описані вказівники у пам'яті комп’ютера мають однаковий об‘єм.
Вказівники можна переадресовувати. Зокрема, у прикладі 2 вказівнику rist спочатку присвоєно адресу змінної studl, а потім - змінної stud3. Це правило не діє, якщо вказівник є сталою. Сталий вказівник, що вказуватимемо на одну і ту ж адресу, оголошують так:
const int *rik;
Для вказівників залежно від операційної системи та версії компілятора резервується 2 або 4 байти в оперативній пам’яті.
Над вказівниками визначені арифметичні операції та операції порівняння, описані в табл. 6.
Таблиця 6. Операції, визначені над вказівниками
Операція |
Приклади і пояснення |
==, !=, >=,>=, >, < |
Порівнює значення двох вказівників (адреси, на які вони вказують). Наприклад, якщо вказівники вказують на одне і те ж саме дане, то результатом порівняння vk1 == vk2 буде істина, інакше - хибність |
|
|
vk1-vk2. Використовується для визначення кількості елементів, які наявні між двома вказівниками |
+, - |
vk1 + k, vk2 – k. Знаходить вказівник, який зміщений відносно даного на k одиниць |
Динамічна пам’ять. Команди new і delete. Значення даних у пам’яті комп’ютера можна зберігати в області даних (статична пам’ять), у стеку або в динамічній пам’яті ("на купі"). Усі змінні, які ми розглядали досі, - статичні. Під час їх оголошення система автоматично надає для зберігання їхніх значень певний обсяг оперативної пам’яті, і цей розподіл пам’яті залишається незмінним протягом виконання програми. Пам’ять, надана цим змінним, резервується (фіксується) у середині exe-файлу відкомпільованої програми. Навіть якщо не всі змінні будуть використані у програмі, пам’ять буде зарезервована, тобто використана неефективно. Пам’ять, надана таким змінним, вивільняється лише після виконання програми. Тому в оперативній пам’яті можна розмістити лише обмежену кількість даних.
Однак є задачі, де заздалегідь невідомо, скільки змінних потрібно для їхнього розв’язування, а, отже, який обсяг пам’яті потрібно зарезервувати, або задачі, у яких, навпаки, заздалегідь відомо, що змінних буде багато, наприклад, задачі опрацювання масивів (див. далі) великих розмірів. У таких випадках застосовують динамічну організацію пам’яті.
Принцип динамічної організації пам’яті полягає у тому, що для змінних надається пам’ять за необхідністю (за вказівкою програміста). Далі ці змінні опрацьовують і в потрібний момент пам’ять вивільняють (знову за вказівкою програміста). Такі змінні називаються динамічними.
Для роботи з динамічними змінними використовують вказівники. Для виділення динамічної пам’яті застосовується команда new так:
<тип вказівника> *<назва> = new <тип змінної>;
Дія команди new. Для відповідного типу змінної автоматично надається необхідна неперервна ділянка пам’яті. Команда new повертає обсяг цієї ділянки, а вказівник вказує на її початок. Наприклад, щоб зарезервувати у пам’яті комп’ютера область для зберігання значення цілого типу, застосовують таку команду: int *prt = new int;.
Надати ділянку пам’яті й відразу занести у неї значення можна так:
int *prt2 = new float(3.14);.
У адресу, на яку показує prt2, буде занесено число 3,14.
З динамічною змінною можна виконувати операції, визначені для даних відповідного базового типу.
Після опрацювання динамічних змінних пам’ять необхідно вивільнити, а відповідний вказівник занулити. Якщо цього не зробити, то пам’ять можна вичерпати. Вивільняють пам’ять за допомогою команди
delete <назва вказівника>
Delete лиш звільняє пам’ять, але не змінює вказівник, щоб вказівник не вказував на жодну ділянку пам’яті, його необхідно занулити такою командою:
<назва вказівника> = 0; //менш коректно (NULL)
Значенням (адресою) такого вказівника буде нульова адреса 0x00000000. Тут не може бути розміщене значення жодного даного.
Приклад 8. Розглянемо, як відбувається розподіл пам’яті. Надамо пам’ять для двох змінних цілого типу та присвоїмо їм деякі значення. Пізніше вивільнимо пам’ять.
#include <iostream>
int main()
{
int *c1 = new int; // Готуємо пам’ять для цілого чиcла
*c1 = 5; // Змінна *c1 набуває значення 5
int *c2 = new int(7); // Виділяється пам’ять для c2 і *c2 набуває значення 7
cout « *c1 « "\t" « *c2 « "\n"; // Виводимо 5 та 7
//Переадреcація -c1 вказуватимемо на ту cаму
c1 = c2; // ділянку пам'яті, що і c2
cout « *c1 « "\t” « *c2 « “\n"; // Виводимо 7 та 7
delete(c2); // Пам'ять, надану для c2, вивільняємо
cout « *c1 « “\n”; // Виводимо 7
}
Приклад 9
#include <iostream>
using namespace std;
void main()
{
int i = 17;
int j = 29;
int* p1 = &i;
cout << p1 << endl;
cout
<< *p1 << endl;
*p1 = 103;
cout << i << endl;
int* p2 = p1;
*p2 = 107;
cout << i << endl << *p1 << endl;
i = 207;
cout << *p1 << endl << *p2 << endl;
p2 = &j;
cout << *p2 << endl;
}
на екран виводиться с = 7 d = 7 чому так?
Тому-що оператор new виділяє пам'ять і позначає її як виділену, а оператор Delete просто знімає цю позначку, але не знищує дані. До його виконання у виділеної пам'яті точно не відбудеться ніяких змін без вашого втручання, але після, цю пам'ять в будь-який момент часу може зайняти щось інше. Після виконання оператора видалення, бажано привласнювати змінної адресу NULL, а після до використання, перевіряти, якщо (...! = NULL) Що відбувається у вашому випадку:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int* a = new int(); //виділяємо пам'ять під числову змінну a int* b = a; //b тепер указує на адресу комірки a (не копіює) *a = 5; //змінюємо a на 5, при цьому b теж стане 5 delete a; //видаляємо a int * c = new int(); //створюємо c, вона скоріше всього займе місце a *c = 3; //c стає трійкою delete b; //ми пам'ятаємо що b посилалася на a, а тепер //його місце зайняла c, так що по суті ми видаляємо c int *d = new int(); //створюємо d, вона займає місце вилученої c *d = 7; //установлюємо d 7 //при цьому й "a" і "b" і "c" і "d" усе посилаються на осередок споконвічно "a" //тобто в усіх у них, зараз 7-ка printf("c = %i d = %i", *c, *d);//От вам і результат getch(); |
Довідка 1. У стандарті ANSI мови С у модулях stdlib.h та alloc.h визначені функції malloc і саllос для надання динамічної пам’яті, а також функція free для вивільнення пам’яті. Детальніше про ці функції можна прочитати у довідниках.
Довідка 2. Замість значення NULL можна записати <назва вказівника> = 0.
Вправи
Нехай зроблені оголошення int u = 4, *prt;. Які з наведених нижче записів неправильні? Поясніть чому.
а) prt = &u; в) prt = *u; д) *(&u) = prt;
б) prt = u; r) *prt = u; e) *prt = *(&u).
Розв’яжіть задачу № 1 з розділу "Задачі"вашого варіанта, застосувавши для змінних динамічну пам’ять.
Розв’яжіть задачу № 2 з розділу "Задачі"вашого варіанта, застосувавши для змінних динамічну пам’ять.
11.1.1-11.1.4
11.2.1
11.4.1-11.4.3
http://rsdn.ru/article/cpp/ObjectsAndPointers.xml
Zubenko-Omelchuk 3.10.8-3.10.9, стор.434
Burn Straustrup 5.1, стор. 113, 5.5, стор. 144
