В чем разница между ссылкой и указателем в C++?

«В чем разница между ссылкой и указателем в C++?» — вопрос из категории C++ Core, который задают на 38% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Ссылка (T&) — это псевдоним существующего объекта. Указатель (T*) — это переменная, хранящая адрес памяти.

Основные различия:

  1. Инициализация и переназначение:

    • Ссылка должна быть инициализирована сразу при объявлении и не может быть изменена для ссылки на другой объект после инициализации.
    • Указатель может быть объявлен без инициализации (но это опасно), может быть nullptr, и его значение (адрес) можно менять.
  2. Синтаксис и безопасность:

    • Со ссылкой работают как с обычной переменной, не требуется оператор взятия адреса (&) или разыменования (*). Ссылка не может быть «висячей» (dangling) на этапе компиляции, если инициализирована корректно.
    • Указатель требует явного разыменования (*) для доступа к значению и оператора взятия адреса (&) для получения адреса. Может стать nullptr или указывать на невалидную память.
  3. Арифметика: Указатели поддерживают арифметику (инкремент, декремент), ссылки — нет.

Пример, иллюстрирующий различия:

int main() {
    int a = 10, b = 20;

    // ССЫЛКА
    int& ref = a;   // OK: ref теперь псевдоним для 'a'
    ref = 15;       // Теперь a = 15
    // int& ref2;   // ОШИБКА: ссылка должна быть инициализирована
    // ref = b;     // НЕ меняет объект ссылки! Это присваивание значения b переменной a.

    // УКАЗАТЕЛЬ
    int* ptr = nullptr; // OK: указатель может быть нулевым
    ptr = &a;           // ptr теперь хранит адрес a
    *ptr = 25;          // Теперь a = 25 (разыменование)
    ptr = &b;           // OK: ptr теперь указывает на b
    *ptr = 30;          // Теперь b = 30

    // Арифметика указателей (на массиве)
    int arr[3] = {1, 2, 3};
    int* p = arr;       // p указывает на arr[0]
    ++p;                // p теперь указывает на arr[1]
    // int& r = arr;    // Ошибка: нельзя инициализировать ссылку массивом
    int (&r_arr)[3] = arr; // Но можно ссылку на массив
    return 0;
}

Когда что использовать:

  • Ссылки — когда нужен гарантированно валидный псевдоним, параметры/возвращаемые значения функций, работа с объектами без копирования.
  • Указатели — когда нужна возможность переназначения, опциональность (nullptr), работа с динамической памятью или массивами, низкоуровневое управление.