← Назад к вопросам
Как функция может поменять аргумент?
1.7 Middle🔥 172 комментариев
#Умные указатели и управление памятью#Язык C++
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы изменения аргументов функции
В C++ функция может изменить аргумент несколькими способами. Ключевое различие — передача по значению, по ссылке, и по указателю.
1. Передача по значению (Pass by Value) — БЕЗ изменения
void increment(int x) {
x++; // Изменяется копия, не оригинал
}
int main() {
int a = 5;
increment(a);
cout << a; // Выведет 5, не 6!
}
Почему не работает:
- Функция получает копию значения
- Изменения затрагивают только копию
- Оригинальная переменная остаётся неизменной
2. Передача по ссылке (Pass by Reference) — работает!
void increment(int& x) { // & означает ссылка
x++;
}
int main() {
int a = 5;
increment(a);
cout << a; // Выведет 6 ✓
}
Как работает:
- Функция получает ссылку на переменную
- Ссылка — это alias (псевдоним) оригинальной переменной
- Изменения воздействуют на оригинал
- Нет копирования (эффективно)
Визуально:
Передача по значению: Передача по ссылке:
main(): main():
int a = 5 int a = 5
| |
| copy | reference
v v
increment(int x) increment(int& x)
x = 5 x points to a
x++; // x = 6 x++; // a becomes 6
// a остаётся 5 // a теперь 6
3. Передача по указателю (Pass by Pointer) — работает!
void increment(int* x) { // * означает указатель
(*x)++; // разыменование: * даёт доступ к значению
}
int main() {
int a = 5;
increment(&a); // & берёт адрес переменной
cout << a; // Выведет 6 ✓
}
Как работает:
- Функция получает адрес переменной
- Через разыменование (*) обращаемся к значению
- Изменения воздействуют на оригинал
Сравнение способов
void byValue(int x) { x = 100; } // копия, не меняет original
void byRef(int& x) { x = 100; } // ссылка, меняет original
void byPointer(int* x) { *x = 100; } // указатель, меняет original
int main() {
int a = 10;
byValue(a); // a = 10 (не изменилась)
byRef(a); // a = 100 (изменилась)
byPointer(&a); // a = 100 (изменилась)
}
Практические примеры
Пример 1: Обмен значений (swap)
// ✗ Неправильно — по значению
void swapWrong(int a, int b) {
int temp = a;
a = b;
b = temp;
// Меняются только копии!
}
// ✓ Правильно — по ссылке
void swapByRef(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// ✓ Правильно — по указателю
void swapByPointer(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
swapByRef(x, y);
cout << x << " " << y; // 10 5 ✓
}
Пример 2: Работа с массивами
// Массивы передаются по указателю автоматически
void fillArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] = i * 2;
}
}
int main() {
int data[5];
fillArray(data, 5);
// data изменится, потому что передаётся адрес
}
Пример 3: Множественные возвращаемые значения
// Функция может изменить несколько параметров
void calculate(int x, int y, int& sum, int& product) {
sum = x + y;
product = x * y;
}
int main() {
int a, b;
calculate(3, 4, a, b);
cout << a << " " << b; // 7 12
}
Ссылка vs Указатель
| Аспект | Ссылка | Указатель |
|---|---|---|
| Синтаксис | int& x | int* x |
| Обязательность | ВСЕГДА инициализировать | Может быть nullptr |
| Разыменование | автоматическое | нужен * |
| Переприсваивание | нельзя (всегда ссылается на исходное) | можно менять адрес |
| Выглядит | как обычная переменная | как указатель |
| Безопасность | безопаснее | нужна проверка на nullptr |
int a = 10, b = 20;
// Ссылка
int& ref = a;
ref = b; // a становится 20, ref по-прежнему ссылается на a
ref = 30; // ref не может переприсваиваться, просто меняет значение a
// Указатель
int* ptr = &a;
ptr = &b; // ptr теперь указывает на b
*ptr = 30; // b становится 30
Const ссылка и указатель
// Const ссылка — не меняет, но избегает копирования
void printData(const int& data) {
cout << data; // Можно читать
// data++; // ОШИБКА — const!
}
// Const указатель на данные
void printPtr(const int* data) {
cout << *data; // Можно читать
// (*data)++; // ОШИБКА — const data!
}
// Указатель const (сам указатель константный)
int x = 10;
int* const ptr = &x; // ptr всегда указывает на x
*ptr = 20; // МОЖНО менять значение
// ptr = &y; // ОШИБКА — нельзя менять адрес
Best Practices
1. Используй ссылки для простоты и безопасности:
void process(vector<int>& data) {
data.push_back(100);
}
2. Используй const ссылки для эффективного чтения больших объектов:
void print(const string& text) {
cout << text; // Нет копирования, безопасно
}
3. Используй указатели когда нужна гибкость (nullptr, переприсваивание):
Node* findNode(Node* root, int value) {
// Может вернуть nullptr
// Указатель может менять цель поиска
}
4. Передача по значению только для примитивных типов и маленьких объектов:
void increment(int x) { // OK для int
x++;
}
void processVector(const vector<int>& vec) { // ЛУЧШЕ const&
// Не vector<int>, чтобы избежать копирования
}
Итог
Для изменения аргумента используй:
- Ссылки (T&) — в большинстве случаев
- Указатели (T)* — когда нужна гибкость
- Const ссылки (const T&) — для чтения больших объектов без копирования
Передача по значению не меняет оригинальный аргумент — это важно помнить на собеседовании!