- •1 Основні відомості про вказівники
- •2. Вказівники та посилання
- •Void function_a (char*, int, short*);
- •Int *pi; /* вказівник-змінна на дані типу int */
- •3. Ініціалізація вказівника
- •4. Розіменування та присвоєння
- •5. Void-вказівник
- •6. Операція пересування вказівника
- •7. Використання вказівників з модифікатором const
- •8. Деактивації дії модифікатора
- •9. Подвійний вказівник
- •10. Вказівники і масиви
- •11. Доступ до ділянок динамічної пам'яті
- •12. Звільнення пам'яті
- •8. Операції з вказівниками
- •Int I, *pi; /* pi –змінна-вказівник */
- •13. Динамічні масиви
- •11. Посилання
- •Лабораторный практикум
- •Вопросы без ответов
- •3 Основні операції над вказівниками
- •4 Багаторівнева непряма адресація
- •5 Операції над вказівниками
- •Void main ()
- •Void main()
- •6 Проблеми, пов'язані з вказівниками
- •Int *х; /* змінній-покажчику 'х' виділена оп, але 'х' не містить значення адреси оп для змінної */
- •Int *х; /* х - ім'я покажчика, він одержав оп*/
- •Void main ()
- •9 Масиви
- •1.9.1 Основні поняття
- •Int а[5]; /* оголошення зовнішнього масиву */ main ()
- •9.2 Оголошення та звертання в одновимірних масивах
- •9.3 Оголошення та звертання до багатовимірних масивів
- •Int а[3][4]; /* а - вказівник-константа */
- •10 Масиви покажчиків
- •10.1 Робота з великими масивами
- •Void main()
- •Int *p[200], I, j; clrscr() ;
- •10.2 Вільні масиви та покажчики
- •11 Символьні рядки
- •11.1 Основні відомості про представлення рядків
- •11.2 Функції роботи з рядками
- •1. Функції введення рядків.
- •2. Функції виведення рядків.
- •14 Лекція №14
- •14.1 Вказівники Лекція № 2 - Вказівники та посилання
- •1.2.1. Вказівники
- •6. Вказівники і масиви
- •6.1. Вказівники і адреси
- •6.2. Вказівники і аргументи функцій
- •6.3. Вказівники і масиви
- •6.4. Адресна арифметика
- •6.5. Вказівники символів і функції
- •6.6. Вказівники – не цілі значення
- •6.7. Багатовимірні масиви
- •6.8. Масиви вказівників (вказівники на вказівники)
- •6.9. Ініціалізація масивів вказівників
- •6.10. Вказівники і багатовимірні масиви
- •6.11. Командний рядок аргументів
- •6.12. Вказівники на функції
- •Посібник для початківця про вказівники
- •6. Вказівники і структуровані програмні змінні
- •9. Вказівники типу far
- •10. Вказівники і динамічні змінні (керування пам'яттю)
- •9. Вказівники типу far
- •10. Вказівники і динамічні змінні (керування пам'яттю)
- •8. Вказівники і динамічні змінні (керування пам'яттю)
- •Void* operator new(size_t t)
- •Void operator delete(void* p)
- •Void main()
Int *х; /* змінній-покажчику 'х' виділена оп, але 'х' не містить значення адреси оп для змінної */
*х = 123; /* - груба помилка! */
Таке присвоювання помилкове, тому що змінна-вказівник х не має значення адреси, за яким має бути розташоване значення змінної.
Компілятор видасть попередження:
Warning: Possible use of 'x' before definition
При цьому випадкове (непроініціалізоване) значення вказівника (сміття) може бути неприпустимим адресним значенням! Наприклад, воно може збігатися з адресами розміщення програми або даних користувача, або даних операційної системи. Запис цілого числа 123 за такою адресою може порушити працездатність програми користувача або самої OC. Компілятор не виявляє цю помилку, це повинен робити програміст!
Виправити ситуацію можна за допомогою функції malloc(). Форма звертання до функції malloc() наступна:
ім'я-вказівника = (тип-вказівника) malloc ( об'єм -ОП ) ;
де ім'я-вказівника - ім'я змінної-вказівника, тип-вказівника - тип значення, що повертається функцією malloc;
об'єм-ОП - кількість байтів ОП, що виділяються змінній, яка адресується.
Наприклад:
х = (int *) malloc ( sizeof (int));
При цьому з купи виділяється 2 байти ОП для цілого значення, а отримана адреса його розміщення заноситься в змінну-вказівник х. Значення вказівника гарантовано не збігається з адресами, що використовуються іншими програмами, у тому числі програмами OC. Параметр функції malloc визначає об' єм ОП для цілого значення за допомогою функції sizeof(int). Запис (int *) означає, що адреса, що повертається функцією malloc(), буде розглядатися як вказівник на змінну цілого типу. Це операція приведення типів.
Таким чином, помилки не буде у випадку використання наступних операторів:
Int *х; /* х - ім'я покажчика, він одержав оп*/
х = (int *) malloc ( sizeof(int));
/* Виділена ОП цілому значенню, на яке вказує 'x' */
*х = 123; /* змінна, на яку вказує 'х', одержала значення 123*/
Повернення (звільнення) ОП у купі виконує функція free(). її аргументом є ім'я вказівника, що посилається на пам'ять, що звільняється. Наприклад: free (x);
Щоб уникнути помилок при роботі з функціями не слід повертати як результат їхнього виконання адреси автоматичних (локальних) змінних функції. Оскільки при виході з функції пам'ять для всіх автоматичних змінних звільняється, повернута адреса може бути використаною системою й інформація за цією адресою може бути невірною. Можна повернути адресу ОП, що виділена з купи.
Одна з можливих помилок - подвійна вказівка на дані, розташовані у купі, і зменшення об'єму доступної ОП через незвільнення отриманої ОП. Це може бути для будь-якого типу даних, у тому числі для скаляра або масиву. Розглянемо випадок для скаляра.
Приклад фрагмента програми з подвійною вказівкою і зменшенням об'єму доступної ОП через незвільнення ОП наведений нижче:
#include<alloc.h>
Void main ()
{
/* Виділення ОП динамічним змінним х, у и z: */
int *х = (int *) malloc (sizeof(int)),
*у = (int *) malloc (sizeof(int)),
*z = (int *) malloc (sizeof(int)); /* Ініціалізація значення покажчиків х, у, z;*/
*х = 14; *у = 15; *z = 17; /*Динамічні змінні одержали конкретні цілі значення*/
y=x; /* груба помилка - втрата покажчика на динамічну змінну в без попереднього звільнення її ОП */
}
У наведеному вище прикладі немає оголошення імен змінних, є тільки вказівники на ці змінні. Після виконання оператора y = х; х та у є двома вказівниками на ту саму ОП змінної *х. Тобто *х = 14; і *у = 14. Крім того, 2 байти, виділені змінній, яку адресував y для розміщення цілого значення (*у), стають недоступними (загублені), тому що значення у, його адреса, замінені значенням х. А в купі ці 2 байти для *у вважаються зайнятими, тобто розмір купи зменшений на 2 байти. Відбулося зменшення доступної ОП. Цього слід уникати.
Щоб уникнути такої помилки треба попередньо звільнити ОП, виділену змінній *у, а потім виконати присвоювання значення змінній у. Наприклад:
Чи можна змінній-вказівнику присвоїти значення адреси в операторі оголошення ? Наприклад: int *x = 12345;
Тут константа 12345 цілого типу, а значенням вказівника х може бути тільки адресою, вказівником на байт в ОП. Тому компілятор при цьому видасть повідомлення про помилку: Error PR.CPP 3: Cannot convert 'int to 'int *' Проте не викличе помилки наступне присвоювання: int a[5], *х = а;
Використання вказівників часто пов'язано з використанням масивів різних типів. Кожний з типів даних масивів має свої особливості. Тому далі розглянемо властивості вказівників для роботи з масивами.
Між вказівниками і масивами існує тісний взаємозв'язок. Будь-яка дія над елементами масивів, що досягається індексуванням, може бути виконана за допомогою вказівників (посилань) і операцій над ними. Варіант програми з вказівниками буде виконаний швидше, але для розуміння він складніший.
Як показує практика роботи на Сі, вказівники рідко використовуються зі скалярними змінними, а частіше - з масивами. Вказівники дають можливість застосовувати адреси приблизно так, як це робить ЕОМ на машинному рівні. Це дозволяє ефективно організувати роботу з масивами. Будь-яку серйозну програму, що використовує масиви, можна написати за допомогою вказівників.
Для роботи з масивом необхідно:
1. визначити ім'я масиву, його розмірність (кількість вимірів) і розмір - кількість елементів масиву;
free (у); /* у = х; /* п
