Lecture5-CPP.ppt
- Количество слайдов: 43
Вказівники, посилання, масиви. Структури Проф. Куссуль Н. М.
Вказівники Вказівник - це похідний тип даних, що містить адресу змінної певного типу. n n Для типу Т тип Т* – вказівник на Т Змінна Т* містить адресу об’єкту Т Приклад int k = 5; int* p 1 = &k, *p 2; Адреса змінної k 12 ff 88 p &k k 5
Вказівники і адреси Вказівник містить адресу об’єкта, Забезпечує «непрямий» доступ до цього об’єкта. Приклад int k = 1; int* p; p 1234 ? p=&k; k 1 1234 змінна значення адреса 3
NULL адреса Якщо заздалегідь невідомо, яку адресу зберігає вказівник, то йому присвоюється значення 0, але вказівник не може бути неініціалізований. Вказівник приймає значення 0, якщо : n вичерпана пам’ять, то оператор new повертає 0 n це вказівник на кінець динамічної структури (список, дерево) Вказівник, значення якого рівне 0, називається порожнім. int* p=0; //порожній вказівник
Операції з вказівниками У випадку оголошення змінної оператор * означає, що оголошується вказівник: int *p=0; //оголошення вказівника Оператор розіменування (*) дозволяє отримати значення, що зберігається за адресою, записаною в вказівнику (непряма адресація). У випадку розіменування операція проводиться не над адресою, а над значенням, збереженим за адресою: *p=5; //присвоює значення 5 змінній, що //зберігається за адресою в вказівнику
Приклади int i = 5, j; //оголошення змінних int* p = &i; //p містить адресу змінної i *p = 5; //присвоює змінній i значення 5 j = p; //заборонено (вказівник не може бути перетворений в цілочисельний тип int!) j = *p+1; //j=6 p = &j; //p вказує на j
Приклади
Операція розіменування int *p=&x; // оголошення+ініц. int y=*р; //розіменування *p=10; // x=y=10; - розіменув. x p змінна 1234 1 ? 1234 значення адреса y 1 ? 8
Приклади double A = 5; /*оголошення дійсної змінної подвійної точності */ double *a; /*оголошення вказівника на дійсну змінну */ a = &A; /* присвоєння вказівнику адреси змінної A. a тепер вказує на A */ *a = 10; /*Присвоєння значення тому, на що вказує а. *a — операція розіменування вказівника */ У результаті змінна A отримує значення 10. 9
Посилання (reference) являє собою видозмінену форму вказівника, яка використовується в якості псевдоніму (іншого імені) змінної Посилання не потребують додаткової пам’яті Для визначення посилання використовують символ & (амперсант), який ставитися перед змінноюпосиланням. int k = 5; //оголошення змінної int& alias = k; // створення посилання на об'єкт k Змінні типу посилання можуть використовуватися в наступних цілях: n n n замість передачі у функцію об'єкта за значенням; для визначення конструктора копії; для перевантаження унарних операцій;
Посилання Основне призначення посилання полягає у спрощенні процесу зміни параметрів всередині функції. void swap (int& a, int& b) { int temp; temp = a; a = b; b = temp; } swap(big_alias, small_alias); // big_allias - посилання на змінну big цілочисельного типу int Використання посилань у даному прикладі дозволяє уникнути комбінування змінних-вказівників зі звичайними змінними.
Приклад
Відмінності між посиланнями і вказівниками Неможливо посилатися безпосередньо на об'єкт типу reference після його визначення; кожне згадування його імені посилається безпосередньо на об'єкт, псевдонімом якого є. Як результат першого звернення не можуть бути виконані будь-які арифметичні обчислення, приведення типів, будь-які інші операції, крім копіювання пов'язаних значень в інші посилання. Після створення посилання не може бути перевизначено, що часто виконують з вказівниками. Посилання не можуть мати значення NULL. Кожне посилання відповідає конкретному об'єкту, незалежно від його коректності.
Відмінності між посиланнями і вказівниками Посилання не можуть бути неініціалізовані, оскільки іх неможливо перевизначити. Зокрема, локальні та глобальні змінні повинні бути проініціалізувати там, де вони визначені, а посилання, які є данимичленами сутностей класу, повинні бути ініціалізований в списку конструктора класу. Наприклад int& k; компілятор видасть повідомлення: помилка: 'k' declared as reference but not initialized ('k' оголошена як посилання, але не ініціалізована)
Масиви Масив – похідний тип даних; Масив - спеціальна форма вказівника, яка зв’язана з неперервним фрагментом пам’яті для зберігання індексованих послідовностей значень. Масив являє собою структуру даних, яка дозволяє одній змінної зберігати кілька значень. При оголошенні масиву необхідно вказати тип змінних, що зберігаються в масиві, а також кількість змінних (елементів масиву). Всі елементи всередині масиву повинні бути одного і того ж типу, наприклад, int, float або char.
Масиви Для будь-якого типу Т, T[size] – масив із size елементів типу T. Розмір масиву size повинен бути константою такою, що інформація про кількість елементів масиву необхідна до моменту початку виконання програми. Статичне оголошення масиву int Array [10]; //масив типу int на 10 елементів int Array[] = {1, 2}; //кількість елементів масиву стає відомою транслятору при аналізу ініціалізатора int Array[2] = {1, 2}; //важливо, щоб кількість елементів при ініціалізації не перевищувала значення константи size. int Array[3] = {1, 2}; //часткова ініціалізація масиву, при якій значення отримують перші елементи масиву, значення останнього елемента не визначено
Масиви Динамічне оголошення масиву виконується з використанням оператора new, який відповідає за виділення динамічної пам'яті. int n = 255; // змінна може бути визначена після //початку виконання програми, але до моменту //оголошення масиву int*array = new int[n];
Масиви Оператор delete очищує пам'ять, виділену оператором new, та повинен виконуватися для кожного виклику new, щоб уникнути витоку пам'яті. Після виклику delete об'єкт не може бути більше використаний. delete[] array;
Багатовимірні масиви в C++ розглядаються як масиви, елементами яких є масиви. Визначення багатовимірного масиву має містити інформацію про тип, розмірності і кількості елементів кожної розмірності. int Array 1[10]; //одновимірний масив розмірності 10 int Array 2[20][10]; //20 одномірних масивів розмірності 10 int Array 3[30][20][10]; //30 двовимірних масивів розмірності 20 * 10
Ініціалізатор масиву Список значень int v 1[] = {1, 2, 3, 4}; char v 2[] = {‘a’, ‘b’, 0}; Якщо масив оголошено без вказання розміру, але ініціалізовано списком, то розмір обчислюється автоматично шляхом підрахунку елементів списку! Якщо розмір вкзаується явно, то кількість елементів не повинна перевищувати заданого розміру! char v 3[2] = {‘a’, ‘b’, 0}; // помилка! Якщо в списку недостатньо елементів, інші елементи ініціалізуються нулями! int v[5] = {1, 2, 3}; v[5] = {1, 2, 3, 0, 0};
Ініціалізатор масиву (прод. ) Не існує присвоювання, яке аналогічно ініціалізації: v[3] = {1, 2, 3}; //недопустимо! Багатовимірний масив ініціалізується подібно. int Array[3][3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; //першими ініціалізються елементи з найменшими індексами int Array[3][3][3] = { {{0}}, {{10}, {2, 3}, {9}}, {{7}, {2, 4}, {6, 3, 0}} }; //додаткові фігурні дужки дозволяють ініціалізувати окремі фрагменти багатовимірного масиву, де кожна пара фігурних дужок специфікує значення, пов'язані з певною розмірностю; порожні дужки не допускаются.
Вказівники на масиви Ім’я масива може використовуватися в якості вказівника на його перший елемент int array[5]; //масив із 5 елементів типу int *array[5]; //5 вказівників на об'єкти типу int *array = new int[5]; //вказівник на масив із 5 об'єктів типу int v[] = {1, 2, 3, 4}; int* p 1 = v; int* p 2 = &v[0]; int* p 3 = &v[4]; p 1 p 2 v 1 p 3 2 3 4
Двовимірний масив Масив виду A[N][M] int a[4][5]; int* p; *p = a; a[i][j] = 4; *(p+i*M+j) = 4; j i N * M
Двовимірний масив (прод. ) Розміщення в пам’яті p p+1 p+2 p+3 p+4 p+5 a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1]
Двовимірний масив int** data; data = new int*[N]; // створення рядків for (int j=0; j
Структура Складений тип, який поєднує різні компоненти в єдину іменованну змінну Масив – набір елементів одного типу Структура - набір елементів різних типів struct address { char* name; long int number; char* street; char* city; long zip; };
Структура (прод. ) Елементи структури наз. членами (members) Вони мають індивідуальні імена! Структури використовуються для створення агрегатів, які підходять для опису складних даних
Оголошення змінної типу структури address jb; jb. name = “John Doe”; jb. number = 61; Для ініціалізації структури можна використати ініціалізатор масиву: address jb = {“James Bond”, 61, “Arapahot Str. ”, “Boulder, CO”, 02092};
Доступ до елементів структури Два способи: n n Крапка: змінна_структури. ім’я_елемента Вказівник w (*вказівник_структури). ім’я_елемента w вказівник_структури->ім’я_елемента address my; my. number = 116; address* neighbor; neighbor->number = 120; // або (*neighbor). number = 120;
Операції над структурами Доступ до елементів n n крапка (. ), або вказівники (->) Присвоювання (=) Операція порівняння не визначенa! (==, !=, тощо) Структури можна передавати як аргументи функцій address set_cur(address next){ address prev = current; current = next; return prev; }
Приклади роботи зі структурами struct employee //оголошення структури { char name [64]; long employee_id; } worker; //визначення об'єкта типу employee strcpy(worker. name, "Аді Шамір"); // копіювати ім‘я worker. employee_id = 12345; void show_employee(employee worker) //оголошення функції, що виводить елементи структури { cout << " Name" << worker. name; cout << " id" << worker. employee_id; } show_employee(worker); //виклик функції
Приклади роботи зі структурами void get_employee_id(employee *worker) { cout << "Введите номер служащего: "; cin >> worker->employee_id; } // програма передає змінну worker типу структури у функцію get employee_id за допомогою адреси get_employee_id(&worker) ; Якщо функція змінює елемент структури, то у функцію повинна передаватися адреса та використовувати вказівник на структуру. Для звернення до елемента структури функції слід використовувати наступний формат: value = variable-> member; variable-> other_member = some_value;
Приклади роботи зі структурами void get_employee_id(employee *worker) { cout << "Введите номер служащего: "; cin >> worker->employee_id; } // програма передає змінну worker типу структури у функцію get employee_id за допомогою адреси get_employee_id(&worker) ; Якщо функція змінює елемент структури, то у функцію повинна передаватися адреса та використовувати вказівник на структуру. Для звернення до елемента структури функції слід використовувати наступний формат: value = variable-> member; variable-> other_member = some_value;
Зв'язаний список — структура даних, в якій елементи лінійно впорядковані, але порядок визначається не номерами елементів, а вказівниками, які входять в склад елементів списку та вказують на наступний елемент або на наступний та попередній елементи. Список має «голову» — перший елемент та «хвіст» — останній елемент.
Різновиди зв'язних списків Однобічно зв'язані списки В однобічно зв'язаному списку, який є найпростішим різновидом зв'язаних списків, кожний елемент складається з двох полів: data або даних, та вказівника next на наступний елемент. Якщо вказівник не вказує на інший елемент (інакше: next = NULL), то вважається, що даний елемент — останній в списку.
Різновиди зв'язних списків Двобічно зв'язаний список В двобічно зв'язаному списку елемент складається з трьох полів — вказівника на попередній елемент prev, поля даних data та вказівника next на наступний елемент. Якщо prev=NULL, то в елемента немає попередника (тобто він є «головою» списку), якщо next=NULL, то в нього немає наступника ( «хвіст» списка).
Різновиди зв'язних списків Кільцевий список В кільцевому списку перший та останній елемент зв'язані. Тобто, поле prev голови списка вказує на хвіст списка, а поле next хвоста списка вказує на голову списка.
Застосування списків Списки інтенсивно застосовуються в програмуванні як самостійні структури. Також на їх основі можуть будуватись більш складні структури даних, такі як дерева. На базі списків також можуть бути реалізовані стеки та черги.
Дерево визначається, як скінченна множина Т з однієї або більше вершин (вузлів, nodes), яке задовольняє наступним вимогам: існує один виокремлений вузол — корінь (root) дерева інші вузли (за виключенням кореня) розподілені серед m ≥ 0 непересічних множин T 1…Tm і кожна з цих множин в свою чергу є деревом. Дерева T 1…Tm мають назву піддерев (subtrees) даного кореня.
Стек — різновид лінійного списку, структура даних, яка працює за принципом «останнім прийшов — першим пішов» (LIFO, англ. last in, first out). Всі операції (наприклад, видалення елементу) в стеку можна проводити тільки з одним елементом, який знаходиться на верхівці стеку та був введений в стек останнім.
Черга— динамічна структура даних, що працює за принципом "перший прийшов - перший пішов" (англ. FIFO — first in, first out). У черги є голова (англ. head) та хвіст (англ. tail). Елемент, що додається до черги, опиняється в її хвості. Елемент, що видаляється з черги, знаходиться в її голові.
Переваги та недоліки Зв'язані списки мають серію переваг порівняно з масивами. Зокрема, в них набагато ефективніше (за час О(1), тобто незалежно від кількості елементів) виконуються процедури додавання та вилучення елементів. Масиви набагато кращі в операціях, які потребують безпосереднього доступу до кожного елементу, що у випадку зі зв'язаними списками неможливо та потребує послідовного перебору усіх елементів, які передують даному.