Як і багато інших мов, C++ має вказівники. Ми вже знаємо, що посилання і вказівники схожі, але уявімо їх як рівень, ближчий до внутрішньої роботи нашого компʼютера. Вказівники - це змінні, які зберігають адреси обʼєктів. Їх використовують, щоб безпосередньо взаємодіяти з обʼєктами, що уможливлює динамічне виділення памʼяті та ефективну роботу з даними в C++.
Якщо ми тільки починаємо знайомитися з вказівниками, вони можуть здаватися трохи загадковими, але коли ми до них звикнемо, то побачимо, що вони досить прості.
Вони відіграють важливу роль у C++, тож приділімо їм час, щоб справді їх зрозуміти. Найпростіший варіант у цьому концепті також називають дурним вказівником або сирим вказівником. У сучасному C++ є також розумні вказівники, але базовий тип зовсім не розумний, і всю роботу доводиться робити вручну.
Перш ніж заглиблюватися в деталі, варто зрозуміти, для чого потрібні вказівники. Вказівники - це спосіб поділитися адресою обʼєкта з іншими частинами нашої програми, і це корисно з двох основних причин:
Оголошення вказівника в C++ передбачає зазначення типу даних, на який вказує вказівник, після чого ставиться зірочка (*) та імʼя вказівника.
Коли ми оголошуємо вказівники, вони не ініціалізуються автоматично.
Без явного присвоєння вказівник зазвичай містить невизначене значення, яке часто називають «сміттєвою адресою».
Хоча деякі компілятори можуть ініціалізувати вказівники значенням nullptr, така поведінка не гарантована для всіх компіляторів, тож на неї не варто покладатися.
Найкраща практика - явно ініціалізувати сирі вказівники та перевіряти, що вони не дорівнюють null, перш ніж їх використовувати, щоб уникнути можливих проблем.
int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid
Щоб присвоїти вказівнику адресу змінної, ми використовуємо оператор взяття адреси (&).
Розіменування вказівника виконують за допомогою оператора розіменування (*).
std::string opponent{"Solomon Lane"};
// 'ethan' points to the address of the string opponent
std::string* ethan{&opponent};
// Instead of ethan's, the opponent's name address is given to the passPort
std::string passportName{*ethan};
Увага: розіменування потрібно виконувати явно, тоді як посилання просто працювали як псевдонім.
Арифметика вказівників дає змогу виконувати арифметичні операції над вказівниками, що особливо корисно під час роботи з масивами. Додавання цілого числа до вказівника змушує його вказувати на інший елемент.
// Stargate Coordinate Code
int gateCode[] = {462, 753, 218, 611, 977};
// 'ptr' points to the first element of 'gateCode'
int* ptr{&gateCode[0]};
// Accesses the third Stargate address through pointer arithmetic
int dialedAddress{*(ptr + 2)};
// Chevron encoded! Dialing Stargate address:
openStarGate(dialedAddress);
Арифметика вказівників у C++ легко може призвести до невизначеної поведінки, якщо поводитися з нею необережно. Невизначена поведінка може проявлятися в несподіваних результатах роботи програми, аварійних завершеннях або навіть уразливостях безпеки. Один сумнозвісний приклад наслідків невизначеної поведінки стався 1996 року під час вибуху ракети «Аріан 5», коли програмний виняток, спричинений перетворенням 64-бітного числа з плаваючою комою на 16-бітне ціле зі знаком, призвів до катастрофічної відмови.
У C++ оператор -> використовують для доступу до членів обʼєкта через вказівник на цей обʼєкт.
Це скорочений запис, який спрощує доступ до членів обʼєктів, на які вказують вказівники.
Наприклад, якщо ptr - вказівник на обʼєкт зі змінною-членом x, то замість (*ptr).x можна одразу використати ptr->x.
Цей оператор підвищує читабельність коду та зменшує багатослівність під час роботи з вказівниками на обʼєкти.
Ось короткий приклад із struct Superhero, який має змінну-член superpower.
Функція main створює вказівник dianaPrince на обʼєкт Superhero (який представляє Диво-жінку).
За допомогою оператора -> отримують доступ до змінної-члена superpower, демонструючи знакове «Lasso of Truth» Диво-жінки.
struct Superhero {
std::string superpower;
};
Superhero wonder_woman{};
Superhero* dianaPrince = &wonder_woman;
dianaPrince->superpower = "Lasso of Truth";
// Using the -> operator to access member variable superpower:
std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower;
І вказівники, і посилання дають змогу отримувати непрямий доступ до обʼєктів, але вони різняться своїми можливостями та міркуваннями щодо безпеки. Вказівники дають змогу змінювати обʼєкт, на який вони вказують, і їм можна присвоїти null. Однак така гнучкість створює ризики, наприклад розіменування нульових вказівників або створення висячих вказівників. Посилання ж, навпаки, не можуть бути null і привʼязуються до дійсних обʼєктів під час створення, що дає змогу уникнути цих ризиків. З огляду на їхню безпечнішу природу, посиланням варто віддавати перевагу перед вказівниками, якщо тільки додаткові можливості вказівників не є необхідними.
Вітаємо, інженере! Ми належимо до останніх ветеранів фундації Speedywagon, таємної організації, яка десятиліттями бореться з давніми загрозами на кшталт Людей-стовпів. У процесі цієї роботи ми роками підтримували технологічні системи фундації, побудовані з суміші найновіших технологій і застарілих бібліотек.
Однак останнім часом сенсори, які відстежують активність Людей-стовпів, працюють зі збоями. Системи фундації старі, а код взаємодіє із застарілою бібліотекою C++, яку неможливо оновити. Наше завдання - реалізувати чотири основні функції, які відстежують активність сенсорів Людей-стовпів за допомогою старомодної бібліотеки, побудованої на вказівниках.
Як сучасні інженери C++, ми віддали б перевагу розумним вказівникам, але, на жаль, застарілий код вимагає поваги до старих підходів. Доля людства може залежати від цих вказівників, тож дійте обережно, і нехай енергія Хамон веде нас.
Оскільки показання сенсорів можуть бути величезними, ми наводимо макетний struct, який використовується в справжній бібліотеці. Код уже реалізовано у файлі заголовка.
struct pillar_men_sensor {
int activity{};
std::string location{};
std::vector<int> data{};
};
connection_check)Наше перше завдання - переконатися, що сенсор Людей-стовпів підключено правильно.
Ми не можемо допустити хибних тривог через відʼєднані сенсори.
Ми напишемо функцію connection_check, яка перевіряє, чи дійсний вказівник сенсора, порівнюючи його з nullptr.
pillar_men_sensor.true, якщо вказівник сенсора не є null, і false в іншому разі.pillar_men_sensor* sensor{nullptr};
bool isConnected = connection_check(sensor);
// isConnected => false
activity_counter)Люди-стовпи чатують у тінях, і нам потрібно знати, чи зафіксували сенсори якусь активність.
Ми напишемо функцію activity_counter, яка приймає масив сенсорів і місткість, що вказує на кількість сенсорів у масиві.
pillar_men_sensor sensor_array[3] = {{0}, {101}, {22}};
int totalActivity = activity_counter(sensor_array, 3);
// totalActivity => 123
alarm_control)Не кожен сенсор має спричиняти тривогу: це повинно відбуватися лише за справжньої небезпеки.
Функція alarm_control гарантує, що сенсор спричиняє тривогу, лише якщо рівень його активності більший за 0.
Ця функція також має перевіряти сенсори на null, щоб запобігти збоям системи.
pillar_men_sensor.nullptr. Якщо сенсор дорівнює nullptr, поверніть false.true; інакше поверніть false.pillar_men_sensor db{9008, "songokunoie", {7, 7, 7}};
bool alarm = alarm_control(&db);
// alarm => true
uv_alarm
У цьому завданні ми реалізуємо функцію uv_alarm, щоб визначити, чи потрібно запускати тривогу, виходячи з рівнів ультрафіолетового опромінення та активності сенсора.
Функція uv_alarm має використовувати надану функцію uv_light_heuristic, яка працює з вектором даних і повертає значення на основі певних порогів.
Це макетна версія складного коду, який працюватиме у виробництві, тож не змінюйте інтерфейс.
Визначте функцію uv_alarm у просторі імен speedywagon. Вона має:
pillar_men_sensor як свій параметр.false, якщо вказівник сенсора є null.uv_light_heuristic, передаючи адресу масиву data сенсора.true, якщо значення, повернуте uv_light_heuristic, більше за рівень sensor->activity, інакше повертати false.Зареєструйтеся на Exercism, щоб вивчати й опановувати C++, а також 19 концепцій100 вправ та справжнє наставництво від людей, і все це безкоштовно.