C можна вказувати майже на що завгодно. Мова дозволяє створювати покажчики для примітивних типів, масивів, функцій і типів користувача. Але вказівники на структури — те, де стає цікаво. Вони трапляються надзвичайно часто. Якщо ви пишете код на C, ви, ймовірно, стикатиметеся з ними регулярно.
Ось базовий приклад. Він показує, як визначити структуру і потім створити покажчик на неї.
`
Цей фрагмент коду робить більше, ніж просто надає адресу пам’яті. Він встановлює зв’язок між змінною ptr та структурою p1. Покажчик зберігає адресу структури. Це дозволяє вам побічно звертатися до членів x та y. Це основний шаблон. Ви бачите його у зв’язкових списках, динамічному виділенні пам’яті та аргументах функцій.
Навіщо використовувати вказівники на структури?
Копіювання цілих структур дорого коштує. Якщо структура містить багато даних, передача її за значенням у функцію означає, що компілятор робить повну копію. Це витрачає час та пам’ять. Використання вказівника дозволяє уникнути копіювання. Ви передаєте адресу замість значення. Функція може змінювати вихідні дані без витрат.
Також постає питання динамічного розміру. Масиви мають фіксований розмір на етапі компіляції. Структури власними силами необов’язково вирішують проблему динамічності. Але коли ви поєднуєте структуру з malloc, ви отримуєте гнучке зберігання даних. Ви можете виділити структуру в купі. Покажчик на цю структуру дозволяє керувати її життєвим циклом вручну.
Доступ до членів через покажчики
Як тільки у вас є вказівник, вам потрібно отримати доступ до даних усередині. Ви не можете використовувати оператор точки. Вираз ptr.x не скомпілюється. ptr – це адреса, а не сама структура. Спочатку ви повинні розіменувати його.
Є два способи зробити це. Перший – багатослівний.
`
Дужки тут обов’язкові. Оператор точки має більший пріоритет, ніж оператор розіменування. Без них компілятор спробує звернутися до ptr.x, якого немає.
Другий спосіб чистіший. C надає стрілочний оператор ->. Він поєднує розіменування та доступ до члена в один крок.
`
Цей синтаксис є стандартним. Саме його ви побачите у реальних кодових базах. Він читається природно як «перейти до об’єкта, на який вказує ‘ptr’, і отримати його член ‘x’». Він лаконічний. Він зменшує візуальний шум.
Поширені помилки
Вказівники NULL становлять ризик. Якщо ви оголошуєте вказівник, але не ініціалізуєте його, він вказує на сміття. Доступ до ptr->x для нульового покажчика викликає сегментацію (сегфолт). Ви повинні перевіряти вказівник на NULL перед його використанням.
`
Ця перевірка не є опціональною у надійному коді. Вона запобігає збоям.
Інша проблема – витоку пам’яті. Якщо ви виділяєте пам’ять за допомогою malloc і втрачаєте вказівник, ця пам’ять залишається зарезервованою до кінця програми. Ви повинні відстежувати кожне виділення. free() – ваша відповідальність. Забути про це
Покажчики на структури C часто стають каменем спотикання для розробників, які впевнено володіють базовими покажчиками, але заплутуються в синтаксисі. Розглянемо просте визначення запису для профілю користувача.
`
Тут RecPointer – це просто псевдонім для вказівника на структуру Rec. Коли ви оголошуєте змінну цього типу, ви маєте справу з адресою пам’яті, а не самими даними.
`
Змінна r займає чотири байти в 32-бітовій системі (або вісім у 64-бітовій). Це просто вказівник. Він не містить ім’я, місто чи стан. Щоб зберігати реальні дані, необхідно виділити пам’ять у купі (heap).
`
Цей виклик malloc резервує 45 байт. Це 21 байт для імені, 21 для міста і 3 для стану плюс один байт для заповнення або вирівнювання для відповідності меж пам’яті. Тепер r вказує на допустимий блок пам’яті, який веде себе так само, як структура Rec.
Доступ до членів через розіменування
Для взаємодії з даними необхідно розіменувати покажчик. Саме тут часто виникають помилки, пов’язані із пріоритетами операцій. Ви можете отримувати доступ до членів за допомогою стандартної точкової нотації, але обернути операцію розіменування в дужки.
`
Зверніть увагу на синтаксис. (*r).name – це правильний варіант. Якщо ви напишете *r.name, код не скомпілюється. Чому? Тому що оператор точки має вищий пріоритет, ніж оператор розіменування. Компілятор інтерпретує *r.name як *(r.name). Оскільки r є покажчиком, r.name є неприпустимим синтаксисом, і вираз зазнає невдачі. Дужки змушують спочатку виконати розіменування *r, отримуючи структуру, а потім оператор точки отримує доступ до поля name.
Це втомлює писати. Виглядає захаращено. Це провокує помилки.
Стрілка нотація
C надає чистіший спосіб роботи з цим. Стрілечний оператор -> є синтаксичним цукром для (*pointer).member. Це не інший механізм. Це не новий оператор, який змінює спосіб доступу до пам’яті. Це просто коротший спосіб запису операції розіменування та доступу до члена структури в один крок.
`
Це ідентично strcpy((*r).name, "Leigh"). Це заощаджує два символи. Це позбавляє необхідності використовувати вкладені дужки. Це стандартний спосіб, яким більшість розробників C взаємодіють з покажчиками на структури.
Наслідки для управління пам’яттю
Виклик free(r) є обов’язковим. Пам’ять було виділено з купи. Якщо ви не звільните її, буде витік пам’яті. Сам покажчик r, чотирибайтова змінна, що зберігає адресу, є локальною для кадру стека або має глобальну область видимості, але дані, на які він вказує, знаходяться в іншому місці.
Коли ви використовуєте r->name, ви змінюєте дані на адресу, що зберігається в r. Сам покажчик r залишається незмінним. Адреса не

Виділення пам’яті для масивів «на льоту» є невід’ємною частиною програмування на C, але цього необхідно розуміти, як покажчики взаємодіють із блоками сирої пам’яті. Коли вам потрібний масив фіксованого розміру, розмір якого невідомий на етапі компіляції, стандартного виділення в стеку недостатньо. Доведеться звертатися до купи (heap).
Наведений нижче фрагмент коду демонструє одну з найпоширеніших схем:
`
Тут malloc резервує місце для десяти цілих чисел. Приведення до типу (int *) гарантує, що покажчик відповідає очікуваному типу, хоча сучасні компілятори C часто видають попередження про явні приведення для void*. Потім цикл ініціалізує кожен елемент банкрутом з використанням нотації з індексами. Зрештою, free повертає пам’ять системі.
Але це єдиний спосіб записи.
Ви можете отримати абсолютно той самий результат, замінивши p[i] на арифметику покажчиків:
`
Чому це важливо? Тому що p[i] – це просто синтаксичний цукор для *(p+i). Компілятор обробляє їх ідентично. Якщо ви працюєте з системами, що вбудовуються, або пишете щільні цикли, де важливий кожен такт, знання того, що ці конструкції взаємозамінні, допомагає читати успадкований код і писати свій власний без плутанини.
Проте тут є тонка пастка.
Якщо ви оголошуєте покажчик на тип масиву безпосередньо – наприклад, int(*p)[10] – ви маєте справу з зовсім іншим механізмом. Цей покажчик вказує на весь масив повністю, а не тільки на його перший елемент. Інкремент такого покажчика зрушує його розмір всього масиву, а чи не розмір одного цілого числа. Більшість розробників використовують int*, оскільки це простіше та гнучкіше.
Коли який підхід використовувати
Вибір між нотацією з індексами та явною арифметикою покажчиків часто зводиться до читання та намірів.
- Використовуйте
p[i], коли хочете підкреслити доступ на основі індексу. Це зрозуміліше більшість читачів. - Використовуйте
*(p+i), коли ви займаєтеся низькорівневою маніпуляцією з пам’яттю або повинні уникнути особливостей “розпаду масиву” у складних виразах.
Обидва підходи вимагають ретельного керування пам’яттю. Забули викликати free – витікаєте пам’ять. Викликали її зарано — отримуєте невизначену поведінку. І хоча malloc тут виглядає просто, завжди перевіряйте, що значення, що повертається не дорівнює NULL, перш ніж розіменовувати покажчик.
«Покажчики масиви потужні, але потребують дисципліни. Один неправильний рух — і ви читаєте сміття чи, що гірше, ушкоджуєте пам’ять іншої змінної».
Насправді більшості розробників рідко потрібно писати сиру арифметику покажчиків для простих масивів. Бібліотеки, такі як std::vector у C++, або більш високорівневі абстракції іншими мовами обробляють це автоматично. Але в C? Ви залишаєтеся віч-на-віч із цим завданням.
Саме тому розуміння механіки має значення. Не тільки для здачі співбесід або написання підручників, але й тоді, коли код ламається о 2-й годині ночі, і вам потрібно точно знати, що відбувається в пам’яті.

Коли ви оголошує вказівник на масив цілих чисел, ви не створюєте нічого екзотичного. Це просто стандартний покажчик на int. Магія відбувається за допомогою ‘malloc’. Ви виділяєте блок пам’яті, достатній для потрібної кількості цілих чисел. Потім покажчик вказує на перший елемент цього блоку.
C не дбає про те, як ви до нього звертаєтесь. Ви можете використовувати квадратні дужки, наприклад p[5], або арифметику покажчиків, наприклад *(p + 5). Компілятор вважає їх ідентичними. Ця гнучкість робить динамічні масиви такими корисними для рядків. Ви не вгадуєте розміру. Ви виділяєте стільки пам’яті, скільки потрібно для довжини рядка плюс нуль-термінатор.
Масиви покажчиків проти масивів структур
Навіщо використовувати масив покажчиків, якщо можна використовувати масив структур? Місце. Або, точніше, його відсутність.
Розглянемо структуру Rec із трьома символьними масивами по 81 байту кожен. Це 243 байти на запис. Якщо ви оголосите Rec records[10], ви миттєво зарезервуєте 2430 байт у пам’яті. Все одразу. Навіть якщо ви коли-небудь використовуватимете лише один запис.
Масив покажчиків змінює математику.
`
Сам масив a містить лише 10 покажчиків. У 64-бітовій системі це 80 байт. Це лише мала частина пам’яті, яка потрібна на повних структур. Пам’ять для самих записів залишається невикористаною, доки вона вам не знадобиться.
Ви можете виділяти один запис у міру потреби.
`
Цей шаблон вирішує проблеми, які потребують великого обсягу пам’яті, відкладаючи виділення. Ви платите лише за те, що ви використовуєте. Коли ви закінчите із записом, ви викликаєте free. Покажчик стає висячим, але пам’ять повертається системі.
Структури, що містять покажчики
Структури можуть містити вказівники. Це дозволяє змішувати дані фіксованого розміру з даними змінного розміру одному об’єкті.
Візьмемо запис у адресній книзі. Ім’я, місто та номер телефону можуть мати розумні максимальні довжини. Однак коментар може бути будь-чим: від одного слова до цілого роману. Ви не бажаєте витрачати місце, виділяючи величезний буфер для кожного запису тільки на той випадок, якщо хтось напише довгий коментар.
`
Сама структура Addr невелика

Як поля коментарів обробляють порожні та заповнені записи
Не кожний запис у базі даних містить коментар. Коли поле залишається порожнім, воно не просто пусте. Воно містить покажчик. А саме, 4-байтовий покажчик, який ні на що не вказує. Система розглядає цю відсутність як допустимий стан. Запис залишається повним. Метадані збережені.
Але що відбувається, коли користувач справді вводить текст?
Процес виділення пам’яті змінюється. База даних не резервує фіксований буфер цих коментарів. Вона не передбачає максимальну довжину і не заповнює простір, що залишився, нулями. Це було б марнотратно. Натомість система обчислює точну довжину рядка. Потім вона виділяє рівно стільки байт, скільки потрібно.
Це динамічне виділення є ефективним. Воно запобігає фрагментації через роздування даних. Коротка нотатка займає кілька байт. Довге есе – більше. Вказівник у порожньому записі вказує на нульове посилання, зберігаючи мінімальний обсяг займаного місця. Записи з вмістом розтягуються, щоб вмістити дані. Нічого не витрачається даремно. Нічого не нав’язується.
Чи це єдиний спосіб зберігання тексту? Ні. Але це розумний спосіб балансувати між швидкістю та простором. Ви отримуєте чуйність заголовків фіксованого розміру з гнучкістю даних змінної довжини. Це невелика деталь. Низькорівневий механізм. Але він має значення, коли ви керуєте мільйонами записів. База даних “дихає” легше. Використання диска залишається компактним.
А чи користувач? Він ніколи не бачить покажчика. Він бачить лише свій коментар. Або його відсутність. Складність прихована. Зберігання оптимізовано. В результаті виходить система, яка відчувається легкою, навіть коли дані зростають.





























![[Огляд] Xiaomi MiJia M365 – відмінний [електросамокат] від надійного китайського виробника](https://web-city.org.ua/wp-content/uploads/2018/01/P1160682_1-218x150.jpg)













![[DNS probe finished no Internet] Як виправити помилку?](https://web-city.org.ua/wp-content/uploads/2018/01/1-42-218x150.jpg)




























