Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
T9_Mas_34 (1).doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
700.42 Кб
Скачать

Масиви покажчиків

У С/С++ елементи масивів можуть мати будь-який тип, зокрема, можуть бути покажчиками на будь-який тип (один і той же). Масив, який містить адреси однотипних даних, називають масивом покажчиків.

Синтаксис оголошення масиву покажчиків:

тип *ім’я [розмір].

Наприклад,

int *m_ptr[3]; //масив з 3-х покажчиків на тип int

typedef float *array[5]; //оголошення типу користувача

array a_ptr; //масив з 5-ти покажчиків на тип float

Оголошені вище змінні m_ptr і a_ptr є масивами покажчиків як тип даних, але не є такими як структура даних. Щоб вони перетворилися на структури даних, їх треба ініціалізувати (зв'язати із відповідними змінними через покажчики-зв'язки). Наприклад,

int a=5, b=10, с=15;

int *m_ptr []={&a,&b,&c}.

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

Алгоритми роботи з масивом покажчиків і звичайним масивом зовні дуже схожі. Різниця ж полягає в тому, що розміщення даних в звичайному масиві відповідає їх фізичному порядку слідування в оперативній пам'яті, а масив покажчиків дозволяє сформувати логічний порядок слідування елементів у відповідності з розміщенням покажчиків на них. Тоді зміна порядку слідування (включення, виключення, упорядкування, перестановка), яка в звичайному масиві полягає в переміщенні самих елементів, у масиві покажчиків повинна супроводжуватися операціями над покажчиками на них.

При роботі з масивом покажчиків використовуються два контекста:

  • р[i] - i-й покажчик в масиві;

  • *р[i] - значення змінної, на яку посилається i-ий покажчик в масиві.

Наприклад,

int s=0, a=5, b=10, с=15;

int *p[]={&a,&b,&c}; // ініціалізація масива покажчиків

for (int i=0; i<3; i++)

s=s+*p[i]; // знаходження суми значень змінних a, b, с

При цьому слід розрізняти оголошення масиву (int mas[5]), масиву покажчиків (int *ptr[5]), покажчика на масив (int (*) mas[5]).

При роботі з масивами покажчиків часто використовується ще один похідний тип даних - покажчик на покажчик. Синтаксис його задання:

тип **покажчик.

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

Покажчик на покажчик допускає чотири інтерпретації:

  1. Покажчик на покажчик на окрему змінну (рис. 9). Наприклад,

int a=5, b=10;

int *p=&a;

int **pp=&p;

(**pp)++; // а=6

*pp=&b;

**pp=0; // b=0

Рис.9. Реалізація покажчика на покажчик на окрему змінну

  1. Покажчик на покажчик на лінійний масив змінних (рис. 10). Наприклад,

int a[10]={5}, b[10]={10};

int *p=a;

int **pp=&p;

for (int i=0; i<10; i++) // ініціалізація елементів масиву а

(*pp)[i]=4; // p[i]=4;

*pp= b;

for (int i=0; i<10; i++) // ініціалізація елементів масиву b, починаючи з другого

{ (*pp)++; // p++;

**pp= i; // *p= i;

}

Рис.10. Реалізація покажчика на покажчик на лінійний масив змінних

У виразах, які використовуються в такій структурі даних, присутні пріоритетні дужки, наприклад, (*pp), оскільки операція непрямого звернення на першому рівні передує операції індексації на другому.

Інші два варіанти стосуються роботи з масивами вказівників.

  1. Покажчик на масив покажчиків на окремі об'єкти (рис. 11). Наприклад,

int s=0, a=5, b=10, с=15;

int *p[]={&a,&b,&c,NULL}; // ініціалізація масива покажчиків

int **pp=p;

for (int i=0; i<3; i++) // знаходження суми значень змінних a, b, с

s=s+*pp[i];

Рис.11. Реалізація покажчика на масив покажчиків на окремі об'єкти

Для доступу до об'єктів, що адресуються покажчиками масива покажчиків, використовується природний порядок операцій *pp[i].

Об'єкти, що адресуються покажчиками масива покажчиків, можуть бути логічно пов'язаними між собою (утворювати масив якогось базового типу).

Нехай має місце наступне оголошення змінних:

int a[] = {10,11,12,13};

int *p[] = {a, a +1, a +2, a +3};

int **pp = p;

В результаті породжуються наступні програмні об'єкти:

Посилання на них може мати достатньо складний вид. Так при виконанні операції pp-p отримаємо нульове значення, так як посилання pp і p вказують на початковий елемент масиву покажчиків, пов'язаного з покажчиком p (на елемент p[0]); при зверненні за допомогою посилання **pp отримаємо 10 - це значення першого елемента масиву a; посилання *pp++ дасть значення другого елемента масиву p, тобто адресу другого елемента масиву a. Якщо вважати, що pp = p, то звернення **++pp - це значення другого елемента масиву a (тобто значення 11), операція ++*pp змінить вміст покажчика p[0], таким чином, що він стане рівним значенню адреси елемента a[1].

Щоб правильно інтерпретувати складні посилання, треба керуватися правилом: такі звернення розкриваються зсередини. Наприклад звернення *(++(*pp)) можна розбити на наступні дії: *pp дає значення початкового елемента масиву p[0], далі це значення інкременується ++(*p) у результаті чого покажчик p[0] стане дорівнювати значенню адреси елемента a[1], і остання дія - це вибірка значення за отриманою адресою, тобто значення 11.

Наприклад,

int a[] = {10,11,12,13};

int *p[] = {a,a+1,a+2,a+3};

int **pp = p;

printf("pp=%p *pp=%p p[]=%p %p %p %p \n",pp,*pp,p[0],p[1],p[2],p[3]);

printf("**pp=%d *p[]= %d %d %d %d \n",**pp,*p[0],*p[1],*p[2],*p[3]);

printf("\n**++pp=%d \n",**++pp);

printf("pp=%p *pp=%p p[]=%p %p %p %p \n",pp,*pp,p[0],p[1],p[2],p[3]);

printf("**pp=%d *p[]= %d %d %d %d \n",**pp,*p[0],*p[1],*p[2],*p[3]);

printf("\n++*pp=%p \n",++*pp);

printf("pp=%p *pp=%p p[]=%p %p %p %p \n",pp,*pp,p[0],p[1],p[2],p[3]);

printf("**pp=%d *p[]= %d %d %d %d \n",**pp,*p[0],*p[1],*p[2],*p[3]);

printf("\n*(++(*pp))=%d \n",*(++(*pp)));

printf("pp=%p *pp=%p p[]=%p %p %p %p \n",pp,*pp,p[0],p[1],p[2],p[3]);

printf("**pp=%d *p[]= %d %d %d %d \n",**pp,*p[0],*p[1],*p[2],*p[3]);

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

  1. Покажчик на масив покажчиків на лінійні масиви змінних (рис. 12). Наприклад,

int s=0,a[]={5,6,7,8}, b[]={1,2,3,4}, с[]={5,2,4,8};

int *p[]={a,b,c};

int **pp=p;

for (int i=0; i<3; i++) // знаходження суми елементів масивів a, b, с

for (int j=0; j<4; j++)

s=s+pp[i][j];

Рис.12. Реалізація покажчика на масив покажчиків на лінійні масиви змінних

Масив покажчиків на лінійні масиви змінних є двовимірної структурою даних і використовує подвійну індексацію. Функціонально вона є еквівалентом двовимірного масиву. Наприклад, наступні оголошення змінних

int a[3][3] = {{11,12,13},

{21,22,23},

{31,32,33}};

int * pa[3] = {a, a [1], a [2]};

int * p = a[0];

породжують у програмі об'єкти, представлені на схемі на рис. 13:

Рис. 13 Схема розміщення покажчиків на двовимірний масив

Відповідно до цієї схеми, доступ до елемента двовимірного масиву a[0][0] можна отримати за покажчиками a, p, pa за допомогою наступних посилань: a[0][0], *a, **a[0], *p, **pa , *p[0].

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

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

Приклад. Сортування цілочисельного масиву.

#include <iostream>

#include <string.h>

using namespace std;

const int n=10;

int *masptr[n], //масив покажчиків на int

mas[n]; //числовий масив

void input(); //введення масиву

void output(); //виведення масиву

void sort (); //сортування масиву

//----------------------- головна функцiя ----------------------------------------------

void main()

{ input(); //введення масиву

output(); //виведення масиву

sort(); //сортування масиву

output(); //виведення відсортованого масиву

system("pause");

}

//----------------------- введення масиву -------------------------------------------

void input()

{ cout<<"input "<<n<<" integer elements: ";

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

{ cin>>mas[i];

masptr[i]=&mas[i]; // masptr[i]=&mas[i];cin>>*masptr[i];

}

}

//----------------------- виведення масиву ---------------------------------------------

void output()

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

cout<<*masptr[i]<<" ";

cout<<endl;

}

//----------------------- сортування масиву -------------------------------------------

void sort()

{ int i, k;

do

{ for (k=0, i=0; masptr[i+1]!=NULL; i++)

if (*masptr [i] > * masptr [i+1])

{ double *c;

c = masptr [i];

masptr [i] = masptr [i+1];

masptr [i+1] = c;

k = 1;

}

} while (k);

}

1 Розмір одновимірного параметра-масива вказувати не обов’язково

20

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