Передача указателя или ссылки в функцию

500
04 января 2017, 02:58

Есть следующий код:

#include "stdio.h"
void ptr(int *ptr, int d)
{
  *ptr = d;
}
void ref(int &ref, int d)
{
  ref = d;
}
int main(void) {
    int digit = 154;
    int &a = digit;
    ref(a, 999);
    printf("%d - %d\n", digit, a);
    ptr(&a, 1000);
    printf("%d - %d\n", digit, a);
    return 0;
}

Интересует следующее:

Есть такое понятие как передача по значению, работает ли это понятие когда мы передаем аргумент: как указатель, как ссылку?(Лучше объяснить как и почему)

P.S Мне почему-то кажется что когда мы передаем аргумент как указатель то создается внутренняя переменная (локальная), а значит тратится память и так каждый раз при передаче указателю. С ссылкой пока не ясно.

Answer 1

По сути, все варианты - передача по значению.

При f(T t) по значению передается объект t, для которого создается временная копия.

Очевидно, что при передаче по указателю f(T* t) по значению передается указатель t, для которого создается временная копия. Памяти тратится ровно столько, сколько занимает указатель.

При передаче по ссылке f(T&t) фактически происходит передача указателя. Можно рассматривать эту передачу как передачу по указателю, просто указатель в теле функции везде разыменован, так что не приходится писать то-то типа *t = 5; - грубо говоря, эту * за вас добавляет компилятор. Соответственно, памяти и в этом случае тратится ровно столько, сколько занимает указатель.

Как иллюстрация - https://godbolt.org/g/TSZt1y

Answer 2

Если у вас есть функция, объявленная, к примеру, как

void f( T parm );

где T - это некоторый тип, и функция вызывается с некоторым аргументом arg, как

f( arg );

то это можно представить следующим образом

void f( /* T parm */ )
{
    T parm = arg;
    //...
}

То есть параметры функции - это ее локальные переменные, которые инициализируются аргументами, переданными функции при ее вызове.

Для функций из вашего примера это можно представить следующим образом

Следующее определение функции и ее вызов

void ptr(int *ptr, int d)
{
  *ptr = d;
}
// ...
ptr(&a, 1000);

можно представить как

void ptr( /* int *ptr, int d */ )
{
    int *ptr = &a;
    int d = 1000;
    *ptr = d;
}

То есть переменные ptr и d являются локальными переменными функции, которые после завершения работы функции прекратят свое существование.

То же самое имеет место и для данной функции и ее вызова

void ref(int &ref, int d)
{
  ref = d;
}
//...
ref(a, 999);

Их можно представить следующим образом

void ref(/* int &ref, int d */)
{
    int &ref = a;
    int d = 999;
    ref = d;
}

Поэтому если вы хотите изменить исходный аргумент, то соответствующий параметр функции следует объявлять либо как ссылку на тип объекта, передаваемого в качестве аргумента, либо как указатель на тип объекта, передаваемого в качестве аргумента.

Механизм объявления ссылки в качестве параметра функции обычно заключается в том, что выделяется память для ссылки в функции, и ссылка инициализируется аргументом, который в свою очередь передается через указатель на аргумент.

READ ALSO
Виртуальный массив

Виртуальный массив

Допустим есть два обычных одномерных массива и есть функция принимающая один массивНеобходимо объединить эти два массива в один и передать...

456
Ассемблерная команда LEA

Ассемблерная команда LEA

Не знаю почему, но эта ассемблерная команда не дает мне покоя LEA

456