Адресна арифметика, типи вказівників та операції над ними.

Операція& одержання адреси об’єкта завжди дає однозначний результат, який залежить від розташування об’єкта в пам’яті, а операція розйменування * вказівник залежить не лише від значення вказівника, але й від його типу. Справа у тому, що при доступі до пам‘яті через розйменування вказівника потрібна інформація не лише про розташування, але й про розміри дільниці пам’яті, які будуть використовуватися. Цю додаткову інформацію компілятор одержує з типу вказівника. Явне перетворення типів при роботі з різними вказівниками в одному виразі потрібне для всіх вказівників, окрім тих,що мають тип void *. При використанні вказівника типу void *операція перетворення типів застосовується при замовчуванні. На відміну від інших типів, тип void передбачає відсутність значення. Вказівник типу void *відрізняється від інших вказівників відсутністю відомостей про розмір відповідної йому дільниці пам’яті.

Операції над вказівниками можна згуртувати таким чином:

· операція розйменування чи доступу за адресою (*);

· перетворення типів (приведення типів);

· надання;

· одержання значення адреси (&);

· додавання та віднімання (адитивні операції);

· інкремент чи автозбільшення (++);

· декремент чи автозменшення (--);

· операції відношення (операції порівняння).

Розйменування, приведення типів, надання розглядалися вище. Про одержання адрси вказівника можна сказати: вказівник є об’єкт і тому має адресу відповідної йому дільниці пам’яті. Значення цієї адреси доступне через операцію &.

Віднімання застосовується до вказівників на об’єкти одного типу і до вказівника та цілої константи. Застосовуючи операцію віднімання до двох вказівників одного типу, можна визначати “відстань” між двома дільницями пам’яты. «Выдстань» визначаэться в одиницях, кратних довжины ( у байтах) об’єкта того типу, до якого віднесений вказівник. Таким чином, різниця однотипних вказівників, що адресують два суміжних об’єкти довільного итпу, за абсолютним значенням завжди дорівнює 1.

Подібно до описаного виконується операція додавання вказівника до цілочислового значення. Додавати два вказівники заборонено синтаксисом мови С++.

Декремент та інкремент вказівників не мають ніяких нових особливостей. Як і віднімання одиничної константи, операція – змінює конкретне числове значення вказівника типу type на величину sizeof(type), де type *- тип вказівника. Таким чином, вказівник пересувається до сусіднього об’єкту з меншою адресою. Подібна до описаного вище декременту операція одиничного автозбільшення ++. Залежно від положення ( перед операндом-вказівником чи за ним) виконання унарних операцій ++ та – відбувається або перед або після використання значення вказівника.

Оскільки вказівник – це об’єкт в пам’яті, тому можна визначчати вказівник на вказівник і т.д. скільки завгодно разів. Наприклад, у наступній програмі визначені такі вказівники і через них виконується доступ до начення змінної:

Приклад

#include <iostream.h>

void main()

{ int i=88;

int *pi=&i;

int **ppi=&pi;

int ***pppi=&ppi;

cout<<”\n ***pppi=”<<***pppi; }// Результат - ***pppi = 88

 

У мові С між вказівниками і масивами існує тісний зв'язок. Наприклад, коли оголошується масив у вигляді int array[25], то цим визначається не тільки виділення пам'яті для двадцяти п'яти елементів масиву, але й створення вказівника з ім'ям array, значення якого дорівнює адресі першого за рахунком (нульового) елемента масиву, тобто сам масив залишається безіменним, а доступ до елементів масиву здійснюється через вказівник з ім'ям array. З точки зору синтаксису мови вказівник array є константою, значення якої можна використовувати у виразах, але змінити це значення не можна.

Для доступу до елементів масиву існує два різні способи. Перший спосіб пов'язаний з використанням звичайних індексних виразів у квадратних дужках, наприклад,

array[16]=3;

або

array[i+2]=7;

При такому способі доступу записуються два вирази, причому другий вираз записується у квадратні дужки. Один з цих висловів має бути вказівником, а другий - виразом цілого типу. Послідовність запису цих виразів може бути будь-якою. Тому записи array[16] і 16[array] будуть еквівалентними і позначають елемент масиву з номером шістнадцять.

Інший спосіб доступу до елементів масиву пов'язаний з використанням адресних висловів та операції непрямого доступу у формі

*(array+16)=3;

або

*(array+i+2)=7;

При такому способі доступу адресний вираз доступу до шістнадцятого елемента масиву теж може бути записано різними способами *(array+16)або *(16+array).

При реалізації на комп'ютері перший спосіб приводиться до другого, тобто індексний вираз перетворюється у адресний. Для наведених прикладів array[16] та 16[array] перетворюються в *(array+16).

У мові С не проводиться перевірка меж масивів: таким чином, ніщо не зупинить вас при виході за межі масиву. Якщо вихід за межі масиву відбувається під час виконання оператора присвоювання, то значення можуть присвоїтися іншим змінним або змінити текст програми. Таким чином, на програміста лягає відповідальність за правильність роботи з масивами.

 

Завдання

Написати програму, в якій:

1. Задати масив цілих чисел довжиною згідно варіанту(масив повинен бути динамічним).

2. Елементи масиву задати випадковим чином в межах заданих діапазонів.

3. Вивести вихідний масив на екран.

4. Виконати обробку масиву відповідно до варіанту, вивести оброблений масив на екран.

 

Номер варіанта Довжина масиву Діапазон значень елементів масиву (цілі величини) Завдання
-3…10 Для кожного позитивного елементу масиву знайти, чи є в масиві елементи, рівні йому за модулем, але протилежні за знаком. Якщо є, то змінити знак негативного елементу. Вивести елементи масиву на екран.
-5…10 Визначити, яке абсолютне значення найчастіше зустрічається в масиві і вивести його на екран.
-10…20 Знайти максимальний та мінімальний елементи масиву і поміняти їх місцями. Вивести елементи масиву на екран. ПРИМІТКА. Якщо в масиві є кілька елементів, які відповідають мінімальному або максимального значення, то міняються місцями перші з них.
-10…20 Підрахувати кількість пар сусідніх елементів, які мають протилежні знаки, і вивести ці пари на екран (із зазначенням індексу кожного виведеного елемента). ПРИМІТКА. Вважати нуль позитивним числом.
-11…20 Знайти суму чисел, що знаходяться між максимальним і мінімальним елементами масиву (в суму включити ці елементи). Вивести суму на екран. ПРИМІТКА. Якщо в масиві є кілька елементів, які відповідають мінімальному або максимального значення, то використовувати перші з них.
-5…10 Елемент вектора називається локальним максимумом, якщо він строго більше двох своїх сусідів. Знайти всі локальні максимуми в межах масиву і вивести на екран номери локальних максимумів, індекси масиву їм відповідні, а також значення відповідних елементів масиву.
-5…15 Вивести на екран індекси всіх парних елементів масиву і самі значення цих елементів. ПРИМІТКА. Вважати нуль парним числом.
-8…15 Поміняти місцями 1-й позитивний елемент з останнім позитивним елементом, 2-й - передостаннім і т.д. Вивести елементи масиву на екран. ПРИМІТКА. Вважати нуль позитивним числом.
-10…25 Вивести на екран індекси всіх непарних елементів масиву і самі значення цих елементів. ПРИМІТКА. Вважати нуль парним числом.
-5…15 Для кожного позитивного елементу масиву знайти чи є в масиві елементи, рівні йому за модулем, але протилежні за знаком. Якщо є, то змінити знак і позитивних, і негативних елементів на протилежний. Вивести елементи масиву на екран.
-10…20 Знайти середньоарифметичне чисел, що знаходяться між максимальним і мінімальним елементами масиву (включаючи ці елементи в обчислення середньоарифметичного), і вивести це значення на екран. ПРИМІТКА 1. Якщо в масиві є кілька елементів, які відповідають мінімальному або максимального значення, то виконувати розрахунки для перших з них. ПРИМІТКА 2. Середньоарифметичне розраховувати в дійсному форматі.
1…30 Вивести на екран елементи та індекси елементів, значення яких лежать в межах хоча б в 1,5 рази більше найменшого елемента масиву, але не більше, ніж 0,5 найбільшого елемента масиву.
-10…25 Поміняти місцями 1-й негативний елемент з останнім негативним елементом, 2-й - передостаннім і т.д. Вивести елементи масиву на екран. ПРИМІТКА. Вважати нуль позитивним числом.
-10…20 Вивести на екран елементи та індекси елементів, які менше середнього значення всіх елементів масиву.
-6…26 Визначити суму елементів масиву, кратних п'яти. Вивести цю суму на екран.
-10…20 Знайти суму чисел, що знаходяться між максимальним і мінімальним по модулю елементами масиву (в суму включити ці елементи). Вивести отриману суму на екран. ПРИМІТКА 1. Якщо в масиві є кілька елементів, які відповідають мінімальному по модулю або максимальному по модулю значенням, то виконувати розрахунки для перші з них.
0…18 Вивести на екран значення та індекси елементів, які є степенями 2.
0…16 Визначити суму елементів масиву, кратних трьом. Вивести отриману суму на екран.
-10…20 Замінити кожен елемент масиву на середньоарифметичне між ним і його сусідами справа і зліва ( ). Перший і останній елементи масиву не змінювати. Вивести елементи масиву на екран.
-15…32 Знайти суму унікальних (зустрічаються тільки один раз в масиві) елементів масиву.
-7…15 Елемент вектора називається локальним мінімумом, якщо він строго менше двох своїх сусідів. Знайти всі локальні мінімуми в межах масиву і вивести на екран номери локальних мінімумів, індекси масиву їм відповідні, а також значення відповідних елементів масиву.

Контрольні питання

1. Який тип має результат, що повертається операцією взяття адреси?

2. Який оператор треба використовувати, щоб поміняти значення змінної, знаючи її адресу?

3. Які дії припустимі з вказівниками?

4. Що спільного і в чому відмінність між вказівниками і масивами?

5. Які способи звернення до елементів масиву вам відомі?