Чому вказівники в C важливі: як передача за вказівником дозволяє змінювати реальні дані

1

Більшість розробників стикаються з покажчиками C через специфічний механізм, відомий як передача за покажчиком. Це не просто синтаксична особливість; це фундаментальна причина, через яку функції можуть змінювати зовнішній стан. Ви, мабуть, бачили це раніше, не усвідомлюючи цього. Розглянемо функцію scanf. Щоразу, коли ви викликаєте scanf, ви зобов’язані додавати оператор взяття адреси (& ) перед своїми змінними.

scanf("%d", &myNumber);

Якщо ви опустите амперсанд, програма аварійно завершить роботу. Чому? Тому що scanf чекає адресу у пам’яті, а не значення. Розуміння цієї поведінки розкриває логіку того, як C обробляє параметри функцій. Коли ви передаєте вказівник, ви даєте функції дозволу звернутися до пам’яті та безпосередньо перезаписати дані.

Невдача передачі за значенням

Щоб зрозуміти, чому вказівники необхідні, ми маємо подивитися, що відбувається, коли їх немає. Поширена помилка початківців – спроба написати функцію swap, яка змінює місцями два цілих значення.

Ось наївна реалізація, яка не працює:

`

Запустіть цей код. Висновок покаже 5 10, за яким піде 5 10. Обміну не сталося.

Причина проста. У C аргументи за промовчанням передаються за значенням. Коли викликається swap(a, b), система створює локальні копії a та b, названі i та j. Функція відмінно змінює місцями значення i та j. Але коли функція завершує роботу, ці локальні змінні знищуються. Оригінальні a та b в main залишаються без змін.

Рішення: передача за вказівником

Щоб змінити вихідні змінні, функції потрібен доступ до реальних адрес в пам’яті, а не до копій їх значень. Тут стають незамінними вказівники як параметри функцій.

Ось виправлена реалізація:

`

Тепер висновок буде 5 10, за яким піде 10 5. Обмін спрацював.

Як працює розіменування покажчика

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

  1. Виклик: swap(&a, &b) передає адреси a та b.
  2. Параметри: Підпис функції void swap(int *i, int *j) оголошує i та j як покажчики на цілі числа.
  3. ** Зв’язок: ** Усередині swap i містить адресу a. j містить адресу b.
  4. Розіменування: Оператор *i = *j означає «перейти за адресою пам’яті, що зберігається в i, і записати в нього значення, знайдене за адресою пам’яті, що зберігається в j ».

Візуально уявіть дві коробки, підписані a та b.
a містить 5.
b містить 10.
i – це записка з адресою коробки a, написаною на ній.
j – це записка з адресою коробки b, написаною на ній.

Коли код виконує t=*i, він читає записку i, йде за цією адресою та зчитує значення 5. Він зберігає 5 в t. Потім *i = *j зчитує значення з коробки b (яке дорівнює 10 ) і записує його назад у коробку a. Початкові коробки змінилися.

Небезпека пропуску адреси