У С++ ми можемо створити змінну, яка зберігатиме адресу у пам'яті. По суті, ця змінна вказує на дані, які знаходяться у пам'яті за цією адресою.
Вказівник - змінна, яка зберігає адресу у пам’яті.
Для оголошення вказівника записують тип даних на які він вказує та символ*
, що одначає вказівник на
.
Приклад оголошення вказівника на змінну типу int
:
int answerOfLifeUniverseAndEverything = 42;
int *ptrToAnswer = &answerOfLifeUniverseAndEverything;
У цьому прикладі ми:
- Оголосили змінну типу
int
з ім'ямanswerOfLifeUniverseAndEverything
. - Оголосили вказівник на
int
з назвоюptrToAnswer
. - Ініціалізували вказівник значенням - адресою
answerOfLifeUniverseAndEverything
##Розіменування вказівника
Користуючись вказівником, ми можемо доступитися та змінити дані за адресою пам’яті, яку зберігає вказівник. Для цього використовують оператор *
. Ця операція називається операцією розіменування вказівника. Наприклад:
// Оголошуємо змінну та виводимо її значення
int value = 11;
std::cout << "Value: " << value << std::endl;
// Оголошуємо вказівник на змінну
int *ptrToValue = &value;
// Розіменовуємо вказівник та доступаємось до даних у пам'яті на які вказує ptrToValue
// Як ми бачимо у консольному виводі - ми доступилися до даних змінної value
std::cout << "Value (by deref ptrToValue): " << *ptrToValue << std::endl;
// Тепер розіменовуємо вказівник та змінюємо дані наякі він вказує
// По суті - ми модифікували змінну value через вказівник!
*ptrToValue = 42;
// Переконаємося, що дані змінено
std::cout << "Value (after modifiation): " << value << std::endl;
У консолі після запуску прикладу ми побачимо наступний вивід:
Value: 11
Value (by deref ptrToValue): 11
Value (after modifiation): 42
**Як бачимо з прикладу, ми модифікували значення змінної через вказівник. **
##nullptr
На відміну від посилання, ми можемо також створити вказівник, який не вказує ні на що та присвоїти йому адресу пізніше. Для цього користуються спеціальним значенням, яке вказує на те що вказівник не містить коректної адреси - nullptr
. Наприклад:
std::string *pointerToString = nullptr;
Тепер ми можемо перевірити вказівник на коректне значення
if (pointerToString != nullptr)
{
// Якщо вказівник вказує на рядок - виводимо
std::cout << *pointerToString << std::endl;
}
або просто
if (pointerToString)
{
// Якщо вказівник вказує на рядок - виводимо
std::cout << *pointerToString << std::endl;
}
У останньому випадку відбувається неявне перетворення типу вказівника до bool
(nullptr
- false
, будь яке інше значення - true
).
Так само як і з посиланням, ми можемо передавати параметри функції уникаючи копіювання об’єкту. Цей спосіб передачі називають передачею параметру по вказівнику. Приклад:
void printStringUsingPointer(std::string *pointerToString)
{
// Перевіряємо на nullptr
if (pointerToString)
{
std::cout << *pointerToString << std::endl;
}
else
{
std::cout << "Pointer to string is not initialized!" << std::endl;
}
}
.....
std::string message{"Hello! Hello!"};
// Беремо адресу message та передаємо в якості параметра
printStringUsingPointer(&message);
В загальному завжди вартує перевіряти вказівник на те, чи він ініціалізований адресою (тобто не містить nullptr)