Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C лекції / 4.Покажчики.docx
Скачиваний:
30
Добавлен:
05.03.2016
Размер:
39.45 Кб
Скачать

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 ", pi, *pi, &pi);

    printf ("pc=%%u, *pc=%%c, &pc=%%u ", 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) зберігається в рх, матиме вигляд, зображений на рис. 1.11 зліва:

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

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

#include<stdio.h>

void main()

{

    int i = 123, *pi = &i; /* pi-покажчик на значення типу int */

    printf("розмір покажчика pi = %%d ", sizeof(pi));

    printf("адреса розміщення покажчика pi=%%u ", &pi) ;

    printf("адреса змінної i = %%u ", &i) ;

    printf("значення покажчика pi = %%u ", pi) ;

    printf("значення за адресою pi = %%d ", *pi) ;

    printf("значення змінної i = %%d ", 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 ", *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С++ з приводу такого "підозрілого перетворення" пішов все-таки далі: він просто відмовляється працювати, видаючи повідомлення про помилку. Відповідальність за ініціалізацію покажчиків повністю покладається на програміста, і більш детально про це йтиметься далі.

 Приклад програми із застосуванням покажчиків

#include <iostream>

#include <stdlib.h> // для використання функції malloc()

#include <math.h>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

using namespace std;

// У функцію передаються 3 перших параметра і

// вертаються 2 останніх - вказівникових

int kkr(double a, double b, double c, double *x1, double *x2)

{ // функція обчислення коренів квадратного рівняння

     double d;

     if (== 0) return 0;

     if ((= b*b-4.0*a*c)<0) return -1;

     *x1 = (-b-sqrt(d))/(2*a); // обчислені значення поаертаються

     *x2 = (-b+sqrt(d))/(2*a); // через вказівники у головну програму

     return 1;

}

int main(int argc, char** argv)

{

     double a, b, *x, *y, *z, k1, k2;

     cout << "Input A=";

     cin >> a;

     x = (double *) malloc (sizeof(double));

     // створений вказівник на дійсне число

     cout << "Input X=";

     cin >> *x;

     b = *x; // присвоєне значення введеного дійсного числа

     cout << b << endl; // друк присвоєного значення

     z = x;  // присвоєна адреса (zix показують на спільну область пам'яті)

     cout << *<< endl; // z має те ж значення, що x

     *= a; // присвоєне значення статичної змінної динамічній

     cout << *<< endl; // z змінив своє значення

     cout << *<< endl; // x має теж значення, що і z

     y = &a; // вказівник y показує на змінну a

     cout << *<< endl; // y має теж значення що і a

     // Звернення до підпрограми обчислення коренів квадратного рівняння

     switch(kkr(a, b, *z, &k1, &k2)) // виклик функції як параметру switch

     {

         case 0:cout << "A=0, No roots" << endl; break;

         case 1:cout << "X1= " << k1 << "  X2= " << k2 << endl;

         case -1:cout << "D<0, No roots" << endl; break;

     }

    

 

// Оголошення масиву з наперед невідомою розмірністю

     int i,j,n;

     double *mas; // вказівник на масив дійсних чисел

     cout << "N=";

     cin >> n;

     mas = (double *) malloc (n*sizeof(double));

     // створений вказівник на масив із n дійсних чисел

     for (= 0;< n;i++)

     {

         cout<< "Mas[" << i << "]=";

         cin >> *(mas+i);

     // запис елемента масиву як вказівника зі зміщенням

     }

     // сортування масиву перестановкою елементів

     for (= 0;< n-1;i++)

     for (= 0;< n-1-i;j++)

     if (mas[j] > mas[j+1])

     { // Позначення елементів масиву класичним способом

         a = mas[j];

         mas[j] = mas[j+1];

         mas[j+1] = a;

     }

     // сортування масиву перестановкою елементів

     // аналогічно попередньому

     for (= 0;< n-1;i++)

     for (= 0;< n-1-i;j++)

     if (mas[j] > mas[j+1])

     { // Позначення елементів масиву як вказівників

         a = *(mas+j);

         *(mas+j) = *(mas+j+1);

         *(mas+j+1) = a;

     }

     for (= 0;< n;i++)

     {

         cout << mas[i] << endl; // вивід елементів масиву

     }

     free(mas); // звільнення пам'яті

     return 0;

}