Указатели, адреса и nullptr
Ссылку нельзя перепривязать к другой переменной, и пустой она не бывает. Указатель (pointer) умеет и то и другое. В этой программе один указатель p по очереди меняет две переменные:
#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). Так, например, функция поиска сообщает, что ничего не нашла. Перед разыменованием такой указатель проверяют:
int* found = nullptr;
if (found != nullptr) {
std::cout << *found << '\n';
}За nullptr нет переменной, и читать через него нечего. Разыменование нулевого указателя — неопределённое поведение (undefined behavior). Компилятор такую программу собирает, а стандарт не говорит, что она сделает, — даже падение программы не гарантировано.
Числа подряд
Несколько чисел int, которые лежат в памяти подряд, можно передать функции двумя параметрами: указателем data на первое число и количеством чисел n. Тогда data + i указывает на число с номером i — нумерация начинается с нуля. Запись data[i] — короткая форма *(data + i):
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] выходит за последнее число и тоже даёт неопределённое поведение.
Частая ошибка
Указатель объявили без начального значения и сразу разыменовали:
int main() {
int* p;
*p = 5; // ✗
}warning: variable 'p' is uninitialized when used here [-Wuninitialized]Компилятор только предупреждает. Значение p не задано, и запись по такому адресу — неопределённое поведение. Если адреса ещё нет, инициализируйте указатель значением nullptr: тогда проверка p != nullptr покажет, что он пуст.