Ошибка «переменная не изменилась после вызова функции» — самый частый симптом того, что разработчик перепутал 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&.
💡Константная ссылка — лучший выбор, когда нужно быстро прочитать большой объект, не давая функции его изменить.