Ответ
Вызов функции в C++ — это передача управления от вызывающей функции (caller) к вызываемой (callee). Этот процесс включает несколько шагов на уровне абстрактной машины:
- Вычисление аргументов: Все аргументы вычисляются. Порядок вычисления не специфицирован стандартом.
- Передача аргументов: Каждый аргумент инициализирует соответствующий параметр функции. По умолчанию используется передача по значению (копирование), но может быть передача по ссылке (
&,const &) или по указателю. - Создание кадра стека (stack frame): Выделяется память в стеке для:
- Возвратного адреса (куда вернуться после выполнения).
- Локальных переменных функции (включая параметры).
- Временных объектов, созданных в теле функции.
- Передача управления: Инструкция
callперенаправляет поток выполнения на первую инструкцию тела функции. - Исполнение тела: Выполняется код внутри функции.
- Возврат значения (если есть): Значение из
return-выражения инициализирует объект в точке вызова (может involve вызов конструктора перемещения/копирования или оптимизацию RVO/NRVO). - Уничтожение локальных объектов: Деструкторы локальных объектов вызываются в обратном порядке создания.
- Восстановление кадра: Управление возвращается по сохраненному адресу, стековый указатель корректируется.
Пример с разными способами передачи:
#include <iostream>
#include <string>
void byValue(int x) { x = 10; } // Изменяет копию
void byReference(int& x) { x = 10; } // Изменяет оригинал
void byConstRef(const std::string& s) { std::cout << s.length(); } // Эффективно для тяжелых объектов
int main() {
int a = 5;
byValue(a); // a останется 5
byReference(a); // a станет 10
std::string str = "Hello";
byConstRef(str); // Избегаем копирования всей строки
return 0;
}
Важно: Современные компиляторы активно оптимизируют вызовы функций (inlining), что может полностью устранить накладные расходы.