Обробка масивів
Масиви, як цілісні структури, можуть брати участь тільки в деяких операціях. Зазвичай, це операції "дорівнює" / "не дорівнює". Деякі мови підтримують для змінних-масивів операції присвоєння, коли однією операцією усім елементам масиву присвоюються значення відповідних елементів іншого масиву. У цьому випадку відповідні масиви повинні бути ідентичними за структурою (мати однакові типи індексів і типи компонентів). Всі інші дії виконуються тільки з елементами масивів відповідно до їх типу.
Типовим способом доступу до елементів масиву у мовах програмування є звертання за індексами. РБНФ-нотація звертання до елемента масиву за індексом у С/С++ має вид:
елемент_масиву = ім’я”[“ індекс ”]” { ”[ “індекс”]”}.
Як індекси можуть використовуватися довільні вирази, що включають цілочисельні константи і змінні допустимих порядкових типів. Наприклад,
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].