Разница между аргументами функций, объявленными с помощью & и * в С++
Я набрал следующий пример:
#include <iostream>
double f(double* x, double* y)
{
std::cout << "val x: " << *x << "\n";
std::cout << "val y: " << *y << "\n";
return *x * *y;
}
double f2(double &x, double &y)
{
std::cout << "val x: " << x << "\n";
std::cout << "val y: " << y << "\n";
return x * y;
}
int main()
{
double a, b;
a = 2;
b = 3;
std::cout << f(&a, &b) << "\n";
std::cout << f2(a, b) << "\n";
return 0;
}
В функции f
объявляю x и y в качестве указателей, из которых я могу получить значение, используя *x
. При вызове f
мне нужно передать адрес моих переданных аргументов, поэтому я передаю &a, &b
. f2
то же самое, за исключением того, что определение отличается.
Теперь мой вопрос: они оба одинаково относятся к управлению памятью? Оба не делают никакой копии переданного значения, а вместо этого передают ссылку?
Я задаюсь вопросом о f2
, потому что я не смог прочитать адрес x
в f2
, чтобы я знал больше о x и y в f
(там я знаю адрес и значение).
Спасибо заранее!
Изменить. Хорошо, после нескольких исследований я нашел довольно полезную тему:
Указатель против ссылки
Там также есть ссылка на рекомендации по кодированию google http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments, что довольно useful
Я чувствую (как я понял сейчас, это форма субъекта вкус), чтобы сделать более прозрачным
Ответы
Ответ 1
f2
принимает аргументы по ссылке, которая по существу является псевдонимом для аргументов, которые вы передаете. Разница между указателем и ссылкой заключается в том, что ссылка не может быть NULL. С помощью f
вам нужно передать адрес (используя и оператор) параметры, которые вы передаете указателю, где при передаче по ссылке вы просто передаете параметры и создается псевдоним.
Передача по ссылке const (const double& ref
) предпочтительнее, если вы не собираетесь изменять аргументы внутри функции, а когда вы собираетесь их изменять, используйте неконстантную ссылку.
Указатели в основном используются, когда вам нужно передать NULL
вашим параметрам, очевидно, вам нужно будет проверить его внутри вашей функции, если указатель не был NULL
перед его использованием.
Ответ 2
Это просто синтаксический сахар, чтобы не использовать *
каждый раз, когда вы ссылаетесь на аргумент. Вы все еще можете использовать &
, чтобы иметь адрес x
в f2
.
Ответ 3
Еще одна разница, о которой не упоминалось, заключается в том, что вы не можете изменить то, на что ссылается ссылка. Это не имеет большого значения в примере вызова функции, показанном в исходном вопросе.
int X(10), Y(20);
int *pX = X;
int& rY = Y;
*pX = 15; // change value of X
rY = 25; // change value of Y
pX = Y; // pX now points to Y
rY
всегда указывает на Y
и не может быть перемещен.
Ссылки не могут использоваться для индексирования в простые массивы, например указатели.
Ответ 4
В моей голове параметры функций всегда передаются по значению. Передача int
легко представить, передача double
просто больше, и передача struct
или class
может быть очень большой.
Но передавая указатель на что-то, ну, вы просто передаете адрес по значению. (Указатель часто удобен для процессора, как и int
.)
Ссылка очень похожа, и, конечно, я думаю о ссылке в качестве указателя, но с синтаксическим сахаром, чтобы он выглядел так, как будто объект, о котором он ссылается, был передан по значению.
Вы также можете вспомнить ссылку как указатель const
, то есть:
int i;
int j;
int* p = &i; // pointer to i
int* const cp = p; // cp points to i, but cp cannot be modified
p = &j; // OK - p is modified to point to j
*cp = 0; // OK - i is overwritten
cp = &j; // ERROR - cp cannot be modified
int& ri = i; // ri refers to i
ri = 1; // i is overwritten
ri = j; // i is overwritten again
// Did you think ri might refer to j?
Итак, указатель делает двойное время: это значение само по себе, но оно также может указывать на другое значение, когда вы разыскиваете его, например: *p
.
Кроме того, наличие ссылочных параметров означает, что вы не можете заставить их ссылаться на что-либо еще в течение жизни функции, потому что нет способа выразить это.
Предполагается, что ссылка не может быть инициализирована с помощью null
, но рассмотрим следующее:
void foo(int& i);
int* p = 0;
foo(*p);
Это означает, что указатели должны быть проверены перед их использованием, но ссылки не могут быть проверены. Реализация foo()
может попытаться прочитать или записать на i
, что приведет к нарушению доступа.
В приведенном выше примере указатель p
должен быть проверен перед использованием в вызове foo
:
if (p) foo(*p);
Ответ 5
Вы должны были прочитать адрес x
в обеих функциях.
Чтобы сделать это в f2
, вы обязательно должны префикс x
с помощью &
, так как там x
является ссылкой в double, и вы хотите адрес.
Стоит отметить разницу между ссылками и указателями в том, что первое не может быть NULL. Вы должны передавать что-то (действительное), в то время как при указании указателя вы должны указать в документации, если передача NULL разрешена/определена правильно.
Еще одно отличие - вопрос читаемости: использование ссылок вместо указателей (когда это возможно) делает код менее загроможденным с помощью *
и ->
.