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

3. Основні операції над вказівниками

       Мова Сі надає можливість використання адрес змінних програми за допомогою основних операцій - & та *:

За допомогою основних операцій можна отримати значення адреси змінної а використовуючи непряму адресацію - одержати значення змінної за її адресою.

Призначення цих операцій:

& ім'я змінної - одержання адреси; визначає адресу розміщення значення змінної визначеного типу;

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

       Оператор присвоювання значення адреси вказівнику має вигляд:

       Ім'я_змінної_вказівника = & ім'я змінної;

Наприклад:

int i, *pi; /* pi -змінна вказівник */

pi = &i; /* pi одержує значення адреси 'i' */

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

       Операції * і & можна писати впритул до імені операнду або через пробіл. Наприклад: &і, * pi.

Непряма адресація змінної за допомогою операції * здійснює доступ до змінної за вказівником, тобто повернення значення змінної, розташованої за адресою, що міститься у вказівнику. Операнд операції * обов'язково повинен бути типу вказівник. Результат операції * - це значення, на яке вказує (адресує, посилається) операнд. Тип результату - це тип, визначений при оголошенні вказівника.

       У загальному вигляді оператор присвоювання, що використовує ім'я вказівника та операцію непрямої адресації, можна представити у вигляді:

ім'я змінної * ім'я-вказівника;

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

       Наприклад:

i= *pi; /* 'i' одержує значення, розташоване за адресою, що міститься в вказівнику 'pi' */

       Як і будь-які змінні, змінна pi типу вказівник має адресу і значення. Операція & над змінною типу вказівник: &pi - дає адресу місця розташування самого вказівника, pi - ім'я вказівника визначає його значення, a *pi - значення змінної, що адресує вказівник.

       Звичайно, усі ці значення можна надрукувати. Наприклад, за допомогою наступної програми:

#include <stdio.h> void main() {     char c = 'A';     int i = 7776;     int *pi = &i;     char *pc = &c;     printf ("pi=%u,*pi=%d, &pi=%u\n", pi, *pi, &pi);     printf ("pc=%u, *pc=%c, &pc=%u\n", pc, *pc, &pc); }        У результаті виконання буде виведено:

pi = 65522, *pi = 7776, &pi = 65520 pc = 65525, *рс = А, &pc = 65518        Одне з основних співвідношень при роботі з вказівниками - це симетричність операцій адресації та непрямої адресації. Вона полягає в тому, що: &х == х, тобто вміст за адресою змінної х є значення х. Наприклад, оголошення вказівника pi і змінних i та j:

int *pi, i = 123, j; pi = &i; /*-присвоювання вказівнику значення адреси i */ j = *pi; /* - присвоювання j вмісту за адресою pi */

       Тут змінна j отримує вміст, розташований за адресою змінної i, тобто значення змінної, що адресує вказівник pi: j = * pi = * &i = i;. Два останніх вищенаведених оператора виконують те саме, що один оператор: j = i.

       Для повного остаточного розуміння процесів, що відбувається у пам'яті при маніпуляції з вказівниками, розглянемо ще такий фрагмент:

void func() {     int х;     int *pх; /* pх - вказівник на змінну типу int*/     pх= &х ; /* адреса змінної х заноситься в рх*/     *pх=77; /* число зберігається за адресою, на яку вказує рх */ }

       Розглянемо цей приклад на конкретному малюнку: функція займає область пам'яті, починаючи з адреси 0х100, х знаходиться за адресою 0х102, а рх - 0х106. Тоді перша операція присвоювання, коли значення &х(0х102) зберігається в рх, матиме вигляд, зображений на рис. 2 зліва:

Наступну операцію, коли число 77 записується за адресою, яка знаходиться в рх та дорівнює 0х102 (адреса х), відображає рис. 2 справа. Запис *рх надає доступ до вмісту комірки, на яку вказує рх.

       Далі наведений приклад програми виводу значень вказівника і вмісту, розташованого за адресою, що він зберігає.

#include<stdio.h> void main() {     int i = 123, *pi = &i; /* pi-вказівник на значення типу int */     printf("розмір вказівника pi = %d\n", sizeof(pi));     printf("адреса розміщення вказівника pi=%u\n", &pi) ;     printf("адреса змінної i = %u\n", &i) ;     printf("значення вказівника pi = %u\n", pi) ;     printf("значення за адресою pi = %d\n", *pi) ;     printf("значення змінної i = %d\n", i) ; }

       Результати виконання програми:

розмір вказівника pi = 2 адреса розміщення вказівника pi = 65522 адреса змінної i= 65524 значення вказівника pi = 65524 значення за адресою pi = 123 значення змінної i = 123

       Вказівники можна використовувати:

1. у виразах, наприклад, для одержання значень, розташованих за адресою, що зберігається у вказівнику;

2. у лівій частині операторів присвоювання, наприклад:

    a. для одержання значення адреси, за якою розташоване значення змінної;

    b. для одержання значення змінної.

       Наприклад, якщо pi - вказівник цілого значення (змінної i), то *pi можна використовувати в будь-якому місці програми, де можна використовувати значення цілого типу. Наприклад:

int i = 123, j, *pi; pi = &i; /*pi у лівій частині оператора присвоювання */ j = *pi + 1; /*-це еквівалентно: j = i + 1; pi-у виразі правої частини оператора присвоювання*/        Виклик значення за вказівником можна використовувати також як фактичні параметри при звертанні до функцій. Наприклад:

d = sqrt ((double) *pi); /* *pi - фактичний параметр */ fscant (f, "%d", pi ); /* pi - фактичний параметр */ printf ("%d\n", *pi ); /* *pi - фактичний параметр */        У виразах унарні операції & і *, пов'язані з вказівниками, мають більший пріоритет, ніж арифметичні. Наприклад:

*рх = &х; у = 1 + *рх; /*-спочатку виконується '*', потім '+' */        Останній оператор еквівалентний: у = 1 + х;

       Для звертання до значення за допомогою вказівника-змінної його можна використовувати в операторі присвоювання скрізь, де може бути ім'я змінної. Наприклад, після виконання оператора: рх = &х; цілком еквівалентними є такі описи:

Оператор: Його еквівалент: Або: *рх =0; х = 0; *рх += 1; *рх = *рх + 1; х = х + 1; (*рх)++ ; *рх = *рх + 1; х = х + 1; (*рх)--; *рх = *рх - 1; х = х - 1;

       Наступна програма демонструє найпростіше практичне використання вказівників, виводячи звичайну послідовність літер алфавіту:

#include <stdio.h> char c; /* змінна символьного типу*/ main() {     char *pc; /* вказівник на змінну символьного типу*/     pc=&c;     for(c='A';c<='Z';c++)     printf("%c",*pc);     return 0; }

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

"Suspicious pointer conversion in function main()"

       На ТС це лише попередження (підозріле перетворення вказівника у функції main()(?!)), і якщо на нього ніяк не відреагувати, то програма працюватиме й надалі (адже помилку зафіксовано не буде) і залишається лише здогадуватися, який результат буде надалі. Зазначимо, що компілятор BС++ з приводу такого "підозрілого перетворення" пішов все-таки далі: він просто відмовляється працювати, видаючи повідомлення про помилку. Відповідальність за ініціалізацію вказівників повністю покладається на програміста, і більш детально про це йтиметься далі.

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