Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lek6_cpp_ukr.rtf
Скачиваний:
4
Добавлен:
18.11.2019
Размер:
340.33 Кб
Скачать

6. Проблеми, пов'язані з вказівниками

     Проблеми, пов'язані з вказівниками, виникають при некоректному використанні вказівників. Усі застереження щодо некоректного використання вказівників відносяться до мови Сі так само, як і до багатьох інших низькорівневих мов програмування. Некоректним використанням вказівників може бути:

   • спроба працювати з неініціалізованим вказівником, тобто з вказівником, що не містить адреси ОП, що виділена змінній;

   • втрата вказівника, тобто значення вказівника через присвоювання йому нового значення до звільнення ОП, яку він адресує;

   • незвільнення ОП, що виділена за допомогою функції malloc();

   • спроба повернути як результат роботи функції адресу локальної змінної класу auto (про функції та класи змінних йтиметься далі);

     Запит на виділення ОП з купи робиться за допомогою функцій calloc() та malloc(). Повернення (звільнення) ОП робиться за допомогою функції free().      Розглянемо деякі проблеми, пов'язані з вказівниками.

     При оголошенні вказівника на скалярне значення будь-якого типу оперативна пам'ять для значення, що адресується, не резервується. Виділяється тільки ОП для змінної-вказівника, але вказівник при цьому не має значення. Якщо вказівник має специфікатор static, то ініціюється початкове значення вказівника, рівне нулю. Приклад ініціалізації вказівників нульовими значеннями при їх оголошенні:

static int *pi, *pj; /* pi = NULL; pj= NULL; */

     Розглянемо приклад, що містить грубу помилку: спробу працювати з непроініціалізованим вказівником.

int *х; /* змінній-вказівнику 'х' виділена ОП, але 'х' не містить значення адреси ОП для змінної */ *х = 123; /* - груба помилка! */

     Таке присвоювання помилкове, тому що змінна-вказівник х не має значення адреси, за яким має бути розташоване значення змінної.

     Компілятор видасть попередження:

Warning: Possible use of 'x' before definition

     При цьому випадкове (непроініціалізоване) значення вказівника (сміття) може бути неприпустимим адресним значенням! Наприклад, воно може збігатися з адресами розміщення програми або даних користувача, або даних операційної системи. Запис цілого числа 123 за такою адресою може порушити працездатність програми користувача або самої OC. Компілятор не виявляє цю помилку, це повинен робити програміст!

     Виправити ситуацію можна за допомогою функції malloc(). Форма звертання до функції malloc() наступна:

ім'я-вказівника = (тип-вказівника) malloc ( об'єм -ОП ) ;

де ім'я-вказівника - ім'я змінної-вказівника, тип-вказівника - тип значення, що повертається функцією malloc;

об'єм-ОП - кількість байтів ОП, що виділяються змінній, яка адресується.

     Наприклад:

х = (int *) malloc ( sizeof (int) );

     При цьому з купи виділяється 2 байти ОП для цілого значення, а отримана адреса його розміщення заноситься в змінну-вказівник х. Значення вказівника гарантовано не збігається з адресами, що використовуються іншими програмами, у тому числі програмами OС. Параметр функції 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 для розміщення цілого значення (*у), стають недоступними (загублені), тому що значення y, його адреса, замінені значенням х. А в купі ці 2 байти для *у вважаються зайнятими, тобто розмір купи зменшений на 2 байти. Відбулося зменшення доступної ОП. Цього слід уникати.

     Щоб уникнути такої помилки треба попередньо звільнити ОП, виділену змінній *у, а потім виконати присвоювання значення змінній у. Наприклад:

free (у); /* звільнення ОП, виділеної змінної '*у' */

у = х; /* присвоювання нового значення змінній 'у' */

     Чи можна змінній-вказівнику присвоїти значення адреси в операторі оголошення ? Наприклад:

int *x = 12345;

     Тут константа 12345 цілого типу, а значенням вказівника х може бути тільки адресою, вказівником на байт в ОП. Тому компілятор при цьому видасть повідомлення про помилку:

Error PR.CPP 3: Cannot convert 'int to 'int *'

     Проте не викличе помилки наступне присвоювання:

int a[5], *х = а;

     Використання вказівників часто пов'язано з використанням масивів різних типів. Кожний з типів даних масивів має свої особливості. Тому далі розглянемо властивості вказівників для роботи з масивами.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]