Как работает передача параметров в с++
Если создать некую функцию, например:
void SomeFunction(int* ptr){
int** ptr3 {&ptr};
std::cout << ptr3 << std::endl;
}
int main(){
int num{4};
int* ptr{&num};
std::cout << ptr << std::endl;
int** ptr2 {&ptr};
std::cout << ptr2 << std::endl;
SomeFunction(ptr);
}
при передаче указателя ptr как аргумента его ячейка меняется. То есть значение ptr копируется? Если да, то почему такой способ передачи данных быстрее, чем передача по значению, если все равно происходит копирование?
Ответы (1 шт):
То есть значение ptr копируется?
Да, при передачи аргументов копирование происходит всегда. Но может копироваться адрес значения (указатель или ссылка) или само значение.
Все аргументы функции лежат в регистрах и стеке, более того, в момент компиляции (обычно) известны их смещения в этом стеке, что позволяет скомпилировать функцию. Возможности передать туда что-то как-то иначе нет (кроме оптимизации хвостовой рекурсии, но это тут обсуждать не будем - я даже не знаю, есть ли она в плюсах).
то почему такой способ передачи данных быстрее, чем передача по значению
Обычно, когда говорят про передачу по ссылки с целью оптимизации, имеют в виду передачу крупных структур. При передачи указателя передаётся адрес, который занимает 8 байт (ну или 4 байта на x32), а структура может быть гораздо крупнее (плюс, у неё может быть конструктор копирования с выделением памяти и ещё куча всего).
Например, при передачи по значению вектора, в стеке выделяется около 20 байт (пишу на память, точно не помню), плюс выделяется память на все элементы, которые в нём лежат, после чего они так же копируются. Очевидно, что это намного медленнее, чем передать просто 8 байт указателя.
А вот при передаче простых типов в духе int, long, double, никакой экономии нет - наоборот одни минусы.
Доступ к переданным по ссылке данным может быть медленнее из-за двух уровней разыменования, впрочем не всегда так. Помимо этого, для переданного по значению аргумента компилятор может знать, что он изолирован от другого кода, и применять больше оптимизаций к другим данным того же типа (например, sse), тогда как в случае указателя он обязан обеспечить линейность выполнения на случай, если указатель указывает на эти же данные. В си без плюсов есть даже ключевое слово restrict
, которое позволяет помечать указатель, как непересекающийся с другими данными функции, но в плюсах его удалили.