Урок №86. Вказівники і масиви

  Юрій  | 

  Оновл. 7 Вер 2021  | 

 348

У мові С++ вказівники і масиви тісно пов’язані між собою.

Схожість між вказівниками і масивами

Фіксований масив визначається наступним чином:

Для нас це масив з 4 цілих чисел, але для компілятора array є змінною типу int[4]. Ми знаємо, що array[0] = 5, array[1] = 8, array[2] = 6 і array[3] = 4. Але яке значення має сам array?

Змінна array містить адресу першого елемента масиву так, наче це вказівник! Наприклад:

Результат на моєму комп’ютері:

The array has address: 004BF968
Element 0 has address: 004BF968

Зверніть увагу, адреса, що зберігається в змінній array, є адресою першого елемента масиву.

Поширена помилка думати, що змінна array і вказівник на array є одним і тим же об’єктом. Це не так. Хоча обидва вказують на перший елемент масиву, інформація про тип даних у них різна. У вищенаведеному прикладі типом змінної array є int[4], тоді як типом вказівника на масив є int *.

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

Однак і це не є настільки вагомим аргументом, щоб розглядати фіксовані масиви і вказівники як різні значення. Наприклад, ми можемо розіменувати масив, щоб отримати значення першого елементу:

Зверніть увагу, ми не розіменовуємо фактичний масив. Масив (типу int[4]) неявно конвертується у вказівник (типу int *), і ми розіменовуємо вказівник, який вказує на значення першого елемента масиву.

Також ми можемо створити вказівник і присвоїти йому array:

Це працює через те, що змінна array розпадається у вказівник типу int *, а тип нашого вказівника такий же (int *).

Відмінності між вказівниками і масивами

Однак є випадки, коли різниця між фіксованими масивами і вказівниками має значення. Основна відмінність виникає при використанні оператора sizeof. При використанні в фіксованому масиві, оператор sizeof повертає розмір всього масиву (довжина_масиву * розмір_елементу). При використанні з вказівником, оператор sizeof повертає розмір адреси в пам’яті (в байтах). Наприклад:

Результат виконання програми:

16
4

Фіксований масив знає свою довжину, а вказівник на масив — ні.

Інша відмінність виникає при використанні оператора адреси &. Використовуючи адресу вказівника, ми отримуємо адресу пам’яті змінної вказівника. Використовуючи адресу масива, повертається вказівник на весь масив. Цей вказівник також вказує на перший елемент масиву, але інформація про тип відрізняється. Навряд чи вам коли-небудь доведеться це використовувати.

Передача масивів у функції

Через те, що копіювання великих масивів при передачі у функцію є дуже витратною операцією, C++ не копіює масив. При передачі масиву в якості аргументу в функцію, масив розпадається у вказівник на масив і цей вказівник передається в функцію:

Результат виконання програми:

32
4

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

Результат виконання програми:

32
4

У вищенаведеному прикладі C++ неявно конвертує параметр з синтаксису масиву ([]) в синтаксис вказівника (*). Це означає, що наступні два оголошення функції ідентичні:

Деякі програмісти надають перевагу використанню синтаксису [], так як він дозволяє зрозуміти, що функція очікує масив, а не вказівник на значення. Однак в більшості випадків, оскільки вказівник не знає, наскільки великим є масив, вам доведеться передавати розмір масиву в якості окремого параметру (рядки є винятком, тому що вони нуль-терміновані).

Рекомендується використовувати синтаксис вказівника, оскільки він дозволяє зрозуміти, що параметр оброблятиметься як вказівник, а не як фіксований масив, і певні операції, такі як у випадку з оператором sizeof, виконуватимуться з параметром-вказівником (а не з параметром-масивом).

Порада: Використовуйте синтаксис вказівника (*) замість синтаксису масива ([]) при передачі масивів в якості параметрів у функції.

Передача по адресі

Той факт, що масиви розпадаються на вказівники при передачі у функції, пояснює основну причину, по якій зміна масиву в функції призведе до зміни фактичного масиву. Розглянемо наступний приклад:

Результат виконання програми:

Element 0 has value: 1
Element 0 has value: 5

При виклику функції changeArray(), масив розпадається у вказівник, а значення цього вказівника (адреса пам’яті першого елемента масиву) копіюється в параметр ptr функції changeArray(). Хоча значення ptr в функції є копією адреси масиву, ptr все рівно вказує на фактичний масив (а не на копію!). Тому при розіменуванні ptr, розіменується і фактичний масив!

Цей феномен працює так само і з вказівниками на значення не з масиву. Більш детально цю тему ми розглянемо на відповідному уроці.

Масиви в структурах і класах

Варто згадати, що масиви, які є частиною структур або класів, не розпадаються, коли вся структура або клас передається в функцію.

На наступному уроці ми розглянемо адресну арифметику і поговоримо про те, як насправді працює індексація масиву.

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (6 оцінок, середня: 5,00 з 5)
Loading...

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

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *