Ссылки в C++: короткий гайд для начинающих
Ссылка в C++ — это второе имя для существующей переменной. Разбираем, как объявить ссылку, чем она отличается от указателя и где новичок ломает программу первым делом.

К концу этого гайда вы будете писать функции, которые меняют переданные значения без копирования, и перестанете путать ссылку с указателем. На чтение и повторение примеров уйдёт минут двадцать. Всё, что ниже, работает на любом компиляторе с поддержкой C++11 и новее — g++, clang++ или MSVC.
Что нужно до начала
Ничего экзотического. Достаточно установленного компилятора и умения собрать один файл.
- Компилятор:
g++9+ илиclang++10+. Проверьте командойg++ --version— должна вывести номер версии. - Флаг стандарта при сборке:
-std=c++17. Ссылки есть с самого начала языка, но флаг гарантирует, что примеры со ссылками в циклеforсоберутся без предупреждений. - Понимание, что такое переменная и функция. Указатели знать заранее не обязательно — сравнение с ними будет ниже.
Собирать примеры будем так:
g++ -std=c++17 -Wall main.cpp -o app
./app
Флаг -Wall включает предупреждения — они подскажут, если вы что-то забыли.
Что такое ссылка
Ссылка — это псевдоним, второе имя для уже существующей переменной. Изменили через ссылку — изменилась исходная переменная, потому что это один и тот же объект в памяти.
Шаг 1. Объявите ссылку
Ссылка объявляется через & после типа и обязана быть привязана к чему-то сразу.
#include <iostream>
int main() {
int score = 10;
int& alias = score; // alias — второе имя для score
alias = 42; // меняем через ссылку
std::cout << score << "\n"; // выведет 42
return 0;
}
Соберите и запустите. В терминале должна появиться строка 42. Если вывелось 10 — вы где-то создали копию, а не ссылку; проверьте, что & стоит именно у типа при объявлении alias.
Почему так: alias и score — одна ячейка памяти под двумя именами.
Шаг 2. Передайте аргумент в функцию по ссылке
Главная причина, ради которой ссылки нужны новичку, — менять аргумент внутри функции.
#include <iostream>
// принимает ссылку — работает с оригиналом
void increment(int& value) {
value += 1;
}
int main() {
int counter = 5;
increment(counter);
std::cout << counter << "\n"; // выведет 6
return 0;
}
Запустите — увидите 6. Уберите & из параметра increment(int value), пересоберите: выведется 5, потому что функция получит копию и изменит только её.
Шаг 3. Избегайте копирования тяжёлых объектов
Если объект большой — строка, вектор, свой класс, — передача по значению копирует его целиком. Ссылка передаёт только адрес.
#include <iostream>
#include <string>
// const& — читаем, но не копируем и не меняем
void printName(const std::string& name) {
std::cout << "Привет, " << name << "\n";
}
int main() {
std::string longName = "Александр Константинопольский";
printName(longName); // копии строки не будет
return 0;
}
const std::string& — стандартный способ передать объект в функцию, которая его только читает. Копии не возникает, а const запрещает случайно изменить оригинал.
Ссылка против указателя
Оба дают доступ к чужой переменной, но ведут себя по-разному. Новичку важно не выбрать инструмент по настроению, а понять разницу.
| Свойство | Ссылка int& | Указатель int* |
|---|---|---|
| Может быть непривязанной | Нет, инициализация обязательна | Да, бывает nullptr |
| Перепривязать на другой объект | Нельзя | Можно |
| Синтаксис доступа | Как к обычной переменной | Через * и -> |
| Синтаксис при вызове | func(x) | func(&x) |
| Может указывать «в никуда» | Не должна | Может (риск падения) |
Правило для начала: если объект точно есть и не будет заменён на другой — берите ссылку. Если возможно отсутствие значения — тогда указатель или std::optional.
Где новичок ломает программу
Ссылки простые, пока не встречают время жизни объекта. Разберём частые ошибки с тем, что вы реально увидите.
Возврат ссылки на локальную переменную
// ОПАСНО: local исчезнет после return
int& makeValue() {
int local = 100;
return local; // ссылка на уже несуществующий объект
}
С -Wall компилятор выдаст warning: reference to local variable 'local' returned. При запуске поведение непредсказуемо: мусор, случайное число или падение. Возвращайте по значению — int makeValue().
Забытый & в диапазонном цикле
std::vector<int> nums = {1, 2, 3};
for (int n : nums) n *= 2; // меняет копию, вектор прежний
for (int& n : nums) n *= 2; // меняет элементы вектора
Симптом: вектор после цикла не изменился, хотя код «должен был» его удвоить. Причина — цикл без & работает с копиями элементов.
Ссылка без инициализации
int& broken; // ошибка компиляции
Компилятор скажет error: 'broken' declared as reference but not initialized. Ссылку нельзя объявить пустой — привяжите её сразу.
Изменение через const&
void spoil(const std::string& name) {
name += "!"; // не соберётся
}
Ошибка вида passing 'const std::string' as 'this' argument discards qualifiers. const означает «только чтение». Уберите const, если функция обязана менять аргумент.
Что делать дальше
Освоив базовые ссылки, посмотрите в сторону двух тем. Первая — const& как параметр по умолчанию для любого объекта, который функция только читает: это ваш стандартный выбор в 90% случаев. Вторая — rvalue-ссылки && и move-семантика; это отдельный большой разговор, здесь мы его не разворачивали. Начните с того, что при написании каждой функции спрашивайте себя: копия мне тут нужна или ссылка. В большинстве случаев ответ — ссылка.
Инфографика для маркетплейса: Три главных преимущества товара короткими фразами, выноски в безопасной зоне превью, точный размер площадки.
Спросить за 60 ₽Источники: cppreference — References
Частые вопросы
Чем ссылка принципиально отличается от указателя, если оба дают доступ к переменной?
А если у меня старый компилятор без C++17?
Можно ли создать ссылку на ссылку?
Почему мой цикл for не изменил вектор, хотя код выглядит правильно?
Грозит ли возврат ссылки на локальную переменную падением программы?
Когда лучше передавать по значению, а не по ссылке?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.