Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекція8.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
66.05 Кб
Скачать

Лекція 8. Вказівники

Розуміння і правильне використання вказівників дуже важливо для створення гарних програм на мові С. Вказівники необхідні для успішнго використання функцій і динамічного розподілу пам'яті. Крім того, багато конструкцій мови C++ вимагають застосування вказівників. Однак із вказівниками варто працювати обережно. Використання в програмі неіціалізованого, або "дикого" (wild), вказівника може привести до "зависання" комп'ютера. При неакуратному використанні вказівників у програмі може виникнути помилка, яку дуже важко знайти. Але й обійтися без вказівників у програмах на мові С не можна.

Оголошення вказівників

Вказівник - це змінна, що містить адресу деякого об'єкта. Тут мається на увазі адреса в пам'яті комп'ютера. Взагалі це просто ціле число. Але не можна трактувати покажчик як змінну або константу цілого типу. Якщо перемінна буде вказівнииком, то вона повинна б відповідним чином бути оголошена. Вказівник оголошується в такий спосіб:

тип *<ім'я змінної>;

У цьому оголошенні тип - деякий тип мови С, що визначає тип об'єкта, на який вказує вказівник (адресу якого містить); * - означає, що наступна за ном змінна є вказівником.

Наприклад:

char *ch;

int *temp, i, *j;

float *pf, f;

Тут оголошені покажчики ch, temp, j, pf, змінна i типу int і змінна f типу float.

Операції над вказівниками

З покажчиками зв'язані дві спеціальні операції: & і *.

Обидві ці операції є унарними, тобто мають один операнд, перед якими вони ставляться. Операція & відповідає операції "взяти адресу". Операція * відповідає словам "значення, розташоване по зазначеній адресі".

Особливість мови С перебуває в тому, що знак * відповідає двом операціям, що не мають одна до іншої ніякого відношення: арифметичної операції множення й операції взяти значення. У той же час сплутати їх у контексті програми не можна, тому що одна з операцій унарна (містить один операнд), інша - множення - бінарна (містить два операнда). Унарні операції & і * мають найвищий пріоритет.

У оголошенні зміннної, що є вказівником, дуже важливий базовий тип. Звідкіля компілятор знає, скільки байтів пам'яті займає перемінна, на якій вказує даний покажчик? Відповідь проста: із базового типу вказівника. Якщо вказівник має базовий тип int, то змінна займає 2 байти, char - 1 байт і т.д.

Найпростіші дії з покажчиками ілюструються наступною програмою:

# include <stdio.h>

/* Приклад 27. */

/* Робота з покажчиками */

main()

{

float x=10.1, у;

float *pf;

pf=&x;

y=*pf;

printf(“x=%f у=%f”, х, у);

*pf++;

printf(“x-%f у=%f”, х, у);

y=l+*pf*y;

printf(“x=%f у=%f”, х, у);

return 0;

}

До вказівників можна застосувати операцію присвоювання. Вказівники того самого типу можуть використовуватися в операції присвоювання, як і будь-які інші змінні.

Розглянемо приклад.

# include <stdio.h>

/* Приклад 28. */

main()

{

int x=10;

int *p, *g;

p=&x;

g=p;

printf("%p", p); /* друк вмісту р */

printf("%p", g); /* друк вмісту g */

printf("%d %d", х, *g); /* друк розміру х і розміри за адресою g*/

}

У цьому прикладі приведена ще одна специфікація формату функції printf() %р.Цей формат використовується для друку адреси пам'яті в шістнадцятковій формі.

Не можна створити змінну типу void, але можна створити вказівник на тип void. Вказівнику на void можна привласнити вказівник будь-якого іншого типу. Однак при зворотному присвоюванні необхідно використовувати явне перетворення вказівника на void;

void *pv;

float f, *pf;

pf=&f;

pv=pf;

pf=(float*)pv;

У мові С припустимо привласнюється вказівнику будь-якої адреси пам'яті. Однак, якщо оголошений покажчик на ціле

int *pi;

а за адресою, що привласнений даному вказівнику, знаходиться змінна х типу float, то при компіляції програми буде видане повідомлення про помилку в рядку

р=&х;

Цю помилку можна виправити перетворюючи вказівник на int до типу вказівника на float явним перетворенням типу:

p=(inf*)&x;

Але при цьому втрачається інформація про те, на який тип вказував вихідний покажчик:

# include <stdio.h>

/* Приклад 29. */

/* Неправильні дії з вказівниками */

main()

{

float х=10.1, у;

int *p; р=&х; /* Потім замінимо на p=(int*)&x; */

y=*p;

printf("x=%f y%f \n, x, у);

}

У результаті роботи цієї програми не буде отримана та відповідь, що очікувалася. Змінній у не буде привласнене значення змінній х, тому що будуть оброблятися не 4 байти, як покладено для змінної типу float, а тільки 2 байти, тому що базовий тип вказівника - int.

Як і над іншими типами змінних, над вказівниками можна робити арифметичні операції: додавання і віднімання. (Операції ++ і -- є окремими випадками операцій додавання і віднімання.) Арифметичні дії над покажчиками мають свої особливості. Виконаємо найпростішу программу

# include <stdio.h>

/* Приклад 30. */

main()

{

int *p;

int х;

p=&x;

рrintf("%р %р", p, ++р);

}

Після виконання цієї програми ми побачимо, що при операції ++р значення покажчика р збільшилося не на 1, а на 2. І це правильно, тому що нове значення вказівника повинно вказувати не на наступну адресу пам'яті, а на адресу наступного цілого. А ціле, як ми пам'ятаємо, займає 2 байти. Якби базовий тип вказівника був не int, a double, то були б надруковані адреси, що відрізняються на 8, саме стільки байт пам'яті займає змінна типу double, тобто при кожній операції ++р значення покажчика буде збільшуватись на кількість байтів, що займають змінну базового типу вказівника.

Операції над вказівниками не обмежуються тільки операціями ++ і --. До вказівників можна додавати деяке ціле або віднімати ціле. Нехай вказівник р має значення 2000 і вказує на ціле. Тоді в результаті виконання оператора

р=р+3;

значення вказівника р буде 2006. Якщо ж вказівник р1=2000 був би вказівником на float, то після застосування оператора

р1=р1+10;

значення р1 було б 2040.

Загальна формула для обчислення значення вказівника після виконання операції р=р+n; буде мати вигляд

<р>=<р>+n*<кільк. байт пам'яті базового типу вказівника>

Можна також віднімати один вказівник від іншого. Так, якщо р і p1 - вказівники та елементи того самого масиву, то операція р - р1 дає такий же результат, як і віднімання індексів відповідних елементів масиву.

Інші арифметичні операції над вказівниками заборонені, наприклад не можна скласти два вказівники, помножити вказівник на число і т.д.

Вказівники можна порівнювати. Застосовнао всі 6 операцій:

<, >, <=, >=, = , == і !=.

Порівняння р < g означає, що адреса, що знаходиться в р, менше адреси, що знаходиться в g.

Якщо р и g вказують на елементи одного масиву, то індекс елемента, на який вказує р, менше індексу масиву, на який указує g.

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