Перейти к содержимому
Теория · Ссылки и указатели · урок 2 из 8

Указатели, адреса и nullptr

Ссылку нельзя перепривязать к другой переменной, и пустой она не бывает. Указатель (pointer) умеет и то и другое. В этой программе один указатель p по очереди меняет две переменные:

C++
#include <iostream>

int main() {
    int a = 1;
    int b = 2;
    int* p = &a;
    *p = 10;
    p = &b;
    *p = 20;
    std::cout << a << ' ' << b << '\n';  // 10 20
}

Адрес и разыменование

Указатель — переменная, которая хранит адрес другой переменной, то есть номер её места в памяти. Тип int* читается как «указатель на int». Выражение &a даёт адрес a, поэтому int* p = &a; кладёт в p адрес a.

Выражение *p обозначает саму переменную, адрес которой хранится в p. Такой доступ называют разыменованием (dereferencing): *p = 10; меняет a. Строка p = &b; меняет сам указатель, и следующее *p = 20; меняет уже b.

Символы & и * значат разное в объявлении и в выражении. В объявлении int& — ссылка, а int* — указатель. В выражении &a берёт адрес, а *p разыменовывает указатель.

Нулевой указатель

Указателю, которому пока не на что указывать, присваивают значение nullptr — это нулевой указатель (null pointer). Так, например, функция поиска сообщает, что ничего не нашла. Перед разыменованием такой указатель проверяют:

C++
int* found = nullptr;
if (found != nullptr) {
    std::cout << *found << '\n';
}

За nullptr нет переменной, и читать через него нечего. Разыменование нулевого указателя — неопределённое поведение (undefined behavior). Компилятор такую программу собирает, а стандарт не говорит, что она сделает, — даже падение программы не гарантировано.

Числа подряд

Несколько чисел int, которые лежат в памяти подряд, можно передать функции двумя параметрами: указателем data на первое число и количеством чисел n. Тогда data + i указывает на число с номером i — нумерация начинается с нуля. Запись data[i] — короткая форма *(data + i):

C++
int sum(int* data, int n) {
    int total = 0;
    for (int i = 0; i < n; ++i) {
        total = total + data[i];
    }
    return total;
}

Номера чисел идут от 0 до n - 1. Чтение data[n] выходит за последнее число и тоже даёт неопределённое поведение.

Частая ошибка

Указатель объявили без начального значения и сразу разыменовали:

C++
int main() {
    int* p;
    *p = 5;  // ✗
}
TEXT
warning: variable 'p' is uninitialized when used here [-Wuninitialized]

Компилятор только предупреждает. Значение p не задано, и запись по такому адресу — неопределённое поведение. Если адреса ещё нет, инициализируйте указатель значением nullptr: тогда проверка p != nullptr покажет, что он пуст.