Ошибка «переменная не изменилась после вызова функции» — самый частый симптом того, что разработчик перепутал pass by value и pass by reference, то есть передачу по значению и по ссылке. Термин «rw pass by» в поисковых запросах обычно означает именно это: как функция читает (read) и записывает (write) переданные ей данные, и почему запись иногда «не доходит» до исходной переменной.

Разница между двумя механизмами фундаментальна. При передаче по значению функция получает копию данных и может изменять её сколько угодно — оригинал останется нетронутым. При передаче по ссылке функция работает с самой переменной, и любая запись внутри функции мгновенно отражается снаружи. Понимание этого различия определяет корректность кода, его производительность и предсказуемость поведения программы.

Что такое передача по значению (pass by value)

При передаче по значению вызываемая функция получает независимую копию аргумента. Все операции чтения и записи внутри функции выполняются над этой копией, а не над исходной переменной. После завершения функции копия уничтожается, а оригинал сохраняет прежнее состояние.

Такой механизм безопасен: вызывающий код гарантированно не пострадает от побочных эффектов. Однако у него есть цена — копирование больших структур данных требует времени и памяти. Если в функцию передаётся массив на миллионы элементов или тяжёлый объект, полное копирование может стать узким местом производительности.

В языке C передача по значению — единственный встроенный механизм. В C++ и C# он используется по умолчанию для простых типов, а в Python и Java ситуация тоньше: там передаётся копия ссылки на объект, что по поведению занимает промежуточное положение.

Что такое передача по ссылке (pass by reference)

При передаче по ссылке функция получает не копию, а псевдоним исходной переменной — фактически доступ к той же области памяти. Любая запись через этот псевдоним изменяет оригинал. Именно это и нужно, когда функция должна вернуть больше одного результата или отредактировать переданный объект.

В C++ ссылка объявляется символом & в сигнатуре функции, в C# используются ключевые слова ref и out, а в C аналогичного эффекта добиваются через указатели. Синтаксис различается, но принцип один: функция работает с оригиналом, а не с копией.

⚠️ Внимание: передача по ссылке делает возможными скрытые побочные эффекты. Если функция меняет переданный объект, а вызов выглядит как обычное чтение, отладка такого кода становится тяжёлой. Документируйте намерение изменять аргумент явно.

Сравнение двух механизмов

Чтобы выбрать подходящий способ передачи, полезно видеть различия в одном месте. Ниже — сводная таблица ключевых свойств обоих механизмов.

КритерийПо значениюПо ссылке
Что получает функцияКопию данныхДоступ к оригиналу
Изменение оригиналаНевозможноВозможно
Расход памятиРастёт с размером данныхМинимальный (адрес)
Скорость для больших объектовНизкая из-за копированияВысокая
Безопасность от побочных эффектовВысокаяТребует дисциплины

Из таблицы видно главное следствие: для крупных структур, которые нужно только прочитать, оптимален третий вариант — передача по константной ссылке. Она сочетает скорость ссылки с защитой от записи. В C++ это записывается как const Type&.

💡

Константная ссылка — лучший выбор, когда нужно быстро прочитать большой объект, не давая функции его изменить.