← Назад к вопросам

Как функция может поменять аргумент?

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& xint* 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&) — для чтения больших объектов без копирования

Передача по значению не меняет оригинальный аргумент — это важно помнить на собеседовании!

Как функция может поменять аргумент? | PrepBro