Покажчики та масиви

Покажчики — це змінні, котрі містять адресу пам’яті, роз­поділеної для об’єкта відповідного типу. Усі змін­ні, розглянуті до цього, зберігали якісь значення (дані). Ці дані могли бути різних типів: символьного, цілого, дійсного тощо. При оголошенні змінної-покажчика слід вказати тип даних, адресу яких буде містити змінна, та ім’я покажчика з симво­лом «*».

Загальний формат опису покажчика має вигляд:

тип *ім’я;

де тип — тип значень, на який вказує покажчик;
ім’я — ім’я змінної-покажчика;
«*» — операція над типом, що читається «покажчик на тип».

Наприклад:

int *рn – покажчик на ціле значення;
float *pf1, *pf2; — два покажчики на дійсні значення.

Покажчики не прив’язують дані до якого-небудь визначе­ного імені змінної і можуть містити адреси будь-якого неімено­ваного значення. Існує адресна константа NULL, що означає порожню адресу.

Слід нагадати, що мова C++ налічує лише дві операції, які стосуються адрес змінних, а саме:

«&»операція взяття адреси («адреса значення»);

«*»операція розіменування («значення за адресою»).

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

Оголошення покажчиків можна здійснити одним з таких способів:

<тип> *ptr;
<тип> *ptr = <змінна-покажчик>;
<тип> *ptr = &<ім’я змінної>;.

Наприклад:
int *ptx, b; float у; — оголошені змінна-покажчик ptx та змінні b і у;

float *sp = &у; — покажчику sp присвоюється адреса змінної у;

float *р = sp; — покажчику р присвоюється значення (адреса зна­чення), яке міститься в змінній sp, тобто адреса змінної у.

При оголошенні покажчиків символ «*» може знаходитися перед ім’ям покажчика або відразу після оголошення типу по­кажчика і поширювати свою дію тільки на одну змінну-покаж­чик, перед якою він записаний:

long *pt;   long*Uk;   int *ki, x, h; — оголошення описів.

За потреби для опису покажчика на комірку довільного типу замість ідентифікатора типу записується слово void, а саме:

void *р, *pt; — опис двох покажчиків на довільний тип даних.

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

Для одержання доступу до значення змінної, адреса якої зберігається в покажчику, досить у відповідному операторі про­грами записати ім’я покажчика з символом «*» — здійснити операцію розіменування.

Розглянемо фрагмент програми з поясненнями:

int *р, *р1; — оголошені два покажчики на комірку пам’яті типу int;

int х = 12, у = 5, m[7]; — оголошені змінні х, у і масив m, змінні ініційовані;

р = &у;      // р (&у); — покажчику р присвоєна адреса змінної у.

Якщо для цього фрагмента програми записати оператор ви­ведення у вигляді

cout << “Адрес р ” << р << “Значение по этому адресу = ” << *р;,

то виведеться адреса комірки пам’яті, де записана змінна у і зна­чення цієї змінної (тобто 5).

Використовуючи запис х = *р;, одержимо х = 5, тому щo *р = у = 5;.

Змінити величину параметра у можна так:

у = 10;          // *Р= 10;
*р = *р+5;    //у +=5;.

Остання операція означає збільшення значення змінної у цi­лого типу на 5, тобто у= 15.

При ініціюванні покажчиків їм можна присвоювати або адресу об’єкта (змінної), або адресу конкретного місця пам’яті (масиву), або число 0 (нуль), а саме:

int *pt = (char *) 0x00147; — присвоюється адреса комірки;
int *arrpt = new int [10]; — визначається початкова адреса розмі­щення динамічного масиву;
char *р = 0; — здійснюється ініціювання нулем.

Оскільки покажчики — це спеціальні змінні, то в операці­ях з іншими покажчиками вони можуть використовуватися без символа «*», тобто без розкриття посилання, наприклад:

float *pt1, *pt2, х=15, m[5];
pt1 = &x; 
pt2 = pt1;
pt1 = m;         //pt1 = &m[0];
де m — ім’я масиву, що розглядається як спеціальний покаж­чик-константа.

Приклад 6.6. Написати ілюстраційну програму з використанням покажчиків.

// P6_6.CPP — применение указателей 
#include <iostream.h>
#include <conio.h>
int main ( )
{ int x = 10;
  int *px (&x);   // int *px = &x;
cout << "x =" << x << endl; 
  cout << "*px =" << *px << endl;
  x *= 2;          //x=x*2;
  cout << "Новое значение *px = " << *px << endl;*
  px += 2;       // *px=*px + 2;
  cout << "Результат *px, т. e. x = " << x << endl;
  getch();     //задержка экрана
}

Результат виконання програми:
х = 10 
*рх = 10
Новое значение *рх = 20 
Результат *рх, т. е. х = 22

Для змінної-покажчика існує своя адреса і тому будуть до цільними записи:

int *pt1, *pt2;

pt1 = (int*) &pt2; — покажчику pt1 присвоюється адреса пам’ятi де розташована змінна pt2.

Це має сенс у випадку, коли

int у, *pt1, *pt2 = &у;
pt1 = (int*) & pt2;.

Обмеження на застосування операції взяття адреси:

  • не можна визначати адресу літеральної константи (оскіль­ки для неї не виділяється комірка пам’яті), тобто такий запис, як vp = &345; — неприпустимий;
  • не можна визначати адресу результату арифметичного виразу, тобто запис vp = &(x + y); теж неприпустимий.

Дозволені операції для змінних-покажчиків:

  • операція розіменування «*»;
  • операція взяття адреси «&»;
  • операція присвоювання «=»;
  • операції інкремент «++» і декремент « –»;
  • операції додавання «+» і віднімання «-»;
  • операції відношення (порівняння) покажчиків однакового типу: «==», «!=», «<», «<=», «>», «>=».

У мові C++ масиви і покажчики зв’язані між собою: ім’я масиву визначається як покажчик-константа на початковий (нульовий)елемент масиву. Так, наприклад, при оголошенні одновимірного масиву у вигляді int mas [20]; його ім’я mas – покажчик на адресу початкового елемента масиву &mas[0].

Існує два способи доступу до елементів масиву:

  • з використанням індексу елемента масиву, наприклад, mas[2] або mas[i];
  • з використанням адресного виразу, тобто виразу з по­кажчиком на масив, наприклад, *(mas + 2) або *(mas + і).

Ім’я покажчика на масив можна записати так:

int mas [20];
int *ptr1;
ptr1 = mas;      //ptr1 = &mas[0];,

тут вирази &mas[0] і mas — еквівалентні.

Оскільки в комп’ютері для масивів завжди є суцільний блок комірок пам’яті, в яких розташовуються їх елементи, то адресу наступного елемента mas[1] можна вказати шляхом збільшення покажчика на 1, а саме:

р = &mas[0];

р++;            //р=р  + 1;

Таким чином, адреса і-го елемента визначається як р + і. При цьому з урахуванням типу масиву і відведеної кількості байтів для кожного його елемента автоматично виконується операція збiльшення адреси, тобто:

адреса х[і] = адреса х[0] + i*sizeof (тип); .

Слід зауважити, що для покажчиків, які посилаються на елементи масивів різних типів, результат арифметичних операцій і операцій відношення невизначений.

До двох покажчиків р1 і р2, що вказують на елементи од­ного масиву, застосовують операції відношення: «==», «!=», «<», «<=», «>», «>=». При цьому значення покажчиків роз­глядаються як цілі числа, а результат порівняння дорівнює 0 ( »неправда») або 1 («істина»). Так, відношення вигляду р1<р2 є «істина», якщо р1 указує на більш ранній елемент, ніж р2. Будь-який покажчик можна порівняти з нулем.

В арифметиці з покажчиками можна використовувати ад­ресу неіснуючого «наступного за масивом» елемента. До покаж­чиків можна додавати або віднімати від них цілу величину.

В обох випадках результатом операції буде покажчик на вихід­ний тип, значення якого на вказане число елементів більше або менше вихідного. Тобто, якщо до покажчика р можна додати деяку цілу величину n, а саме: р + n, то цей вираз визначає ділянку об’єкта, що займає n-не місце після об’єкта, на який вказує р, при цьому n автоматично збільшується на коефіцієнт, що дорівнює відповідній довжині об’єкта. Наприклад, якщо int займає 4 байти, то цей коефіцієнт дорівнює чотирьом.

Допускається також операція віднімання покажчиків, що вказують на елементи одного масиву. Так, якщо р1 < р2, то р2 – р1 + 1 — це число елементів масиву від р1 до р2 включно.

Наведемо приклади програм роботи з покажчиками.
Приклад 6.7. Обчислити середнє значення додатних елементів одновимірного масиву.

Розглянемо перший варіант програмної реалізації цієї задачі (див. Р6_7_1.СРР).

/* Р671.СРР — вычисление среднего значения положительных элементов массива */
//---------------- программа без использования указателей
#include <iostream.h>
#include <conio.h>
main( )
{ const int n = 10;
  float mas[n], s = 0;
  int i, kol = 0;
  cout << "Ввод массива " << endl;
  for(i =0; і < n; i++)
    cin >> mas[i]; 
      for(i = 0; і < n; i++)   
        if (mas[i] > 0)  
{ s += mas[i];           //накопление суммы
          kol++;  //подсчет положительных елементов
        }
 cout.precision(3 ) ; 
  cout << "Средн. арифм. полож. элементов = " << s/kol << endl; 
 getch();         //задержка экрана вывода резулътата
}

Результати виконання програми:
Ввод массива
1.56 -4.78 6.5 7.89 -3.6 9.45 7.4 -8.43 9.3 -10.2
Средн. арифм. полож. элементов = 7.02

Використовуючи ім’я масиву як покажчик на початок масиву (перший елемент), можна навести другий варіант програми (див. Р6_7_2.СРР):

// Р6_7_2.СРР - использование имени массива как указателя
#indude <iostream.h>
#include <conio.h>
main ( )
{ const int n = 10;  
  float mas[n], s;  
  int i, kol = 0;
  for (і = 0, s = 0; і < n; i++)
  { сіn >> *(mas+i);
    if (*(mas+i) > 0)
     { s += *(mas+i);
       kol++; }  
  } 
  cout.precision(3); 
  cout <<"\n Среднее арифм. полож. элементов = " << s/kol << endl; 
  getch();
}

Якщо описати покажчик і зв’язати його з масивом (адре­сувати на початок масиву), то з використанням арифметики покажчиків можна написати третій варіант (див. Р6__7_З.СРР) цiєї програми.

// Р6_7_3. СРР — использование арифметики указателей
#include <iostream.h>
#include <conio.h>
main ( )
{ const int n = 10;
  int і, kol(0);
  float mas[n], s(0);
  float *pm = mas;         //pm= &mas[0];
for (і = 0; і < n; i++)  
  { cout << "Введите" << і << "элемент mas" << endl;
    cin >> *pm++;
    cout << mas[i] << endl;
    if (mas[i] >0) 
    { s += mas[i]; 
      kol++; }  
  }
  cout.precision(3);
  cout << "\n Среднее арифм. полож. элементов = " << s/kol << endl;
  getch();
}

У цій програмі для введення масиву застосований покажчик m, а для роботи з масивом — ім’я масиву з індексом.

В останньому випадку використання покажчика m призвело б до помилкового результату, оскільки цей покажчик в опера­ціях введення збільшує свою адресу m++) після введення чергового елемента масиву і надалі вказує на ще не введений елемент.

Наведемо четвертий варіант (Р6_7_4.СРР) програмної реалiзації прикладу:

/* Р6_7_4.СРР — использование указателей
#include <iostream.h>
#include <conio.h>
main ( )
{ const int n = 10;
  float mas[n], s = 0;
  float *pm = &mas[0];     //pm *= &mas[0];
 int i, kol = 0;
  for (і = 0; і < n; i++)
  { cout << "Введите" << і << "элемент mas" << endl;
    cin >> *pm; 
    if (*pm >0)
    { s += *pm; 
      kol++; 
      pm++; }  
  }
  cout.precision(3);
  cout << ”\n Среднее арифм. полож. элементов = " << s/kol << endl;
 getch();
}

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *