
Обробка масивів
Масиви, як цілісні структури, можуть брати участь тільки в деяких операціях. Зазвичай, це операції "дорівнює" / "не дорівнює". Деякі мови підтримують для змінних-масивів операції присвоєння, коли однією операцією усім елементам масиву присвоюються значення відповідних елементів іншого масиву. У цьому випадку відповідні масиви повинні бути ідентичними за структурою (мати однакові типи індексів і типи компонентів).
Всі інші дії виконуються тільки з елементами масивів відповідно до їх типу.
Типовим способом доступу до елементів масиву у мовах програмування є звертання за індексами. РБНФ-нотація звертання до елемента масиву за індексом у С/С++ має вид:
елемент_масиву = ім’я”[“ індекс ”]” { ”[ “індекс”]”}.
Як індекси можуть використовуватися довільні вирази, що включають цілочисельні константи і змінні допустимих порядкових типів. Наприклад,
arr[4], d[4] [5], а[і], m[і+5] [2].
У С/С++ доступ до елементів масиву може здійснюватися не тільки за індексом, а й за покажчиком (варіант з покажчиками в загальному випадку працює швидше).
Для звертання до елементів масиву за покажчиком, треба оголосити відповідний вказівник і ініціалізувати його адресою першого елемента масиву. Наприклад,
int m[3], *p;
p = &m[0]; // або p = m.
Щоб звернутися до будь-якого елементу масиву, вказівник має одержати приріст, кратний розміру елементів масиву. Для цього використовується адресна арифметика.
Операції адресної арифметики:
Інкрементування / декрементування вказівників (“++“ / “--“). Значення вказівника збільшується / зменшується на кількість байт, що визначається типом, на який він вказує. Після виконання відповідної операції покажчик вказує на наступний / попередній елемент масиву.
Скорочене додавання / віднімання цілого числа (“+=“ / “-=“). Значення вказівника збільшується / зменшується на кількість байт, яка необхідна для розміщення заданого числа об’єктів, на які посилається покажчик. Після виконання відповідної операції покажчик зміщується вперед / назад на задану кількість елементів масиву.
Додавання / віднімання цілого числа (“+“ / “-“). Задає логічне зміщення вперед / назад на кількість байт, яка необхідна для розміщення заданого числа об’єктів, на які посилається покажчик.
Обчислення зміщення покажчиків. Якщо є два вказівники на різні елементи одного масиву, то їх можна відняти один від одного і з’ясувати, на якій відстані один від одного знаходяться елементи масиву, на які вказують відповідні покажчики.
Наприклад,
float m[7], n,*р1,*р2;
р1 = &m[0];
р2 = m;
р1 += 5; // зміщення вперед на 4 об’єкта (дійсних числа)
р1 --; // перехід до попереднього елемента масиву
р2 ++; // перехід до наступного елемента масиву
cout<<*(р2+3)<<“\n”; // покажчик-зміщення вперед на 5 об’єктів (дійсних чисел)
cout<<*(р1-2)<<“\n”; // покажчик-зміщення назад на 3 об’єкта (дійсних числа)
р1 -= 1; // зміщення назад на 1 об’єкт (дійсне число)
n = р1 - р2; // зміщення між елементами, на які вказують mPtr1 і mPtr2
Таким чином у С/С++ при роботі з елементами масиву мають місце дві інтерпретації покажчика, розрізнити які в тексті програми можна тільки в контексті використання покажчика:
покажчик як посилання на окрему змінну (цій традиційній інтерпретації відповідає операція непрямого звернення за вказівником *p);
покажчик на пам'ять з відносною адресацією від поточного положення (підтримується операціями адресної арифметики).
Покажчик на масив можна індексувати точно так само, як і масив: при цьому компілятор перетворює індексацію в арифметику покажчиків. Наприклад, звернення до елемента масиву m[3] перетворюється в *(m +3). Тому будь-яке з присвоювань
*m = 2.4;
m [0] = 2.4;
*(m+0) = 2.4;
*р1 = 2.4;
p1[0] = 2.4;
*(р1+0) = 2.4;
виконує одну й ту ж дію: присвоює початковому елементу масива m значення 2.4.
Слід зазначити, що між ім'ям масиву і покажчиком на масив існує одна відмінність: покажчик - це змінна, тому можна написати рtr1 = m або рtr1++; але ім'я масиву не є змінною, і записи на зразок m = рtr1 або m ++ не допускаються.
Оскільки багатовимірні масиви в мові C/С++ - це масиви масивів, тобто масиви, елементами яких, у свою чергу, є масиви, то при оголошенні таких масивів в оперативній пам'яті створюється кілька різних об'єктів (виділяється пам'ять під них):
покажчик на двовимірний масив (його ім'я співпадає з іменем масиву);
масив покажчиків, кожен із яких містить адресу масиву-рядка вихідного двовимірного масиву;
двовимірний масив даних базового типу.
Наприклад, при виконанні оголошення двовимірного масиву int arr[4][3] в програмі створюється покажчик arr, який визначає в пам'яті місце розташування першого елемента масиву і, крім того, є покажчиком на масив з чотирьох покажчиків, кожен з яких містить адресу одновимірного масиву, що представляє собою рядок вихідного двовимірного масиву і складається з трьох елементів типу int (рис. 1).
arr |
|
|||
|
||||
arr [0] |
|
arr [0] [0] |
arr [0] [1] |
arr [0] [2] |
arr [1] |
|
arr [1] [0] |
arr [1] [1] |
arr [1] [2] |
arr [2] |
|
arr [2] [0] |
arr [2] [1] |
arr [2] [2] |
arr [3] |
|
arr [3] [0] |
arr [3] [1] |
arr [3] [2] |
Рис. 1 Розподіл пам'яті для двовимірного масиву |
Доступ до елементів масиву покажчиків здійснюється у формі arr[2] або *(arr+2), до елементів двовимірного масиву чисел типу int - у формі arr[1][2] або еквівалентних їй *(*(arr +1) +2) і (*(arr +1))[2] . Слід враховувати, що з точки зору синтаксису мови С/С++ покажчик arr і покажчики arr[0], arr[1], arr[2], arr[3] є константами і їх значення не можна змінювати під час виконання програми.
Розміщення тривимірного масиву відбувається аналогічно. Так, наприклад, оголошення float mas[3][4][5] породжує в програмі, окрім самого тривимірного масиву з 60 чисел типу float, масив з чотирьох покажчиків на тип float, масив з трьох покажчиків на масив покажчиків на float і покажчик на масив масивів покажчиків на float.
Оскільки елементи багатовимірних масивів розташовуються в пам'яті підряд по рядках, то такий порядок дає можливість звертатися до будь-якого елементу багатовимірного масиву, використовуючи адресу його початкового елемента і тільки один індексний вираз. Наприклад, звернення до елементу arr[1][2] можна здійснити за допомогою покажчика ptr, оголошеного у формі int *ptr = arr[0], як звернення ptr[5].