C++中使用&和*声明函数参数的区别

110

我输入了以下示例:

#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感到困惑,因为我无法读取f2x的地址,所以我对f中的x和y更加了解(在那里我知道地址和值)。

编辑:经过进一步的研究,我找到了一个非常有用的主题:Pointer vs. Reference,其中还有一个链接到Google编码指南http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments,我觉得这很有用(现在我理解了,这是一种主观口味),让我更清楚了解这个问题。

8
这不是C语言,而是C++。 - user395760
啊,我明白了,所以在 C 语言中我总是必须使用指针,好的,谢谢!到目前为止我不知道这一点! - tim
3
谷歌风格指南——避免使用。它在2008年就已经过时了(链接答案的日期),而今天它甚至更糟糕。 - MSalters
@MSalters 避免哪一个?'f'风格还是'f2'风格? - Abhinav
3
避免使用Google的风格指南,它已经非常过时了。 - MSalters
5个回答

97

f2 通过引用来传递参数,这本质上是传递参数的一个别名。指针和引用之间的区别在于引用不能为NULL。对于f,您需要通过使用&操作符来传递参数的地址,而当您通过引用传递参数时,只需传递该参数即可创建别名。

当您不会在函数内更改参数时,通过const引用(const double& ref)进行传递是首选方法;当您将更改它们时,请使用非const引用。

指针主要用于需要能够将NULL传递给参数的情况,显然,在使用之前,您需要在函数内检查指针是否不是NULL


为什么有人需要将Null传递给参数? - Weloo
3
他们可能需要能够在需要时传递null。显然,他们并不总是会这样做。 - Ludwik
2
Q:“指针和引用的区别在于引用不能为NULL”。最简单明了的答案是,在函数参数中何时使用*和&,以及何时不使用。别名永远不会为NULL。 - LXSoft
f2和没有使用&的相同操作(如double f3(double x, double y))有什么区别? - Aaron Franke
@AaronFranke 所以你不会在内存中重复数据 - Mauricio Cortazar

32

这只是语法糖,目的是避免每次引用参数时都要使用*。你仍然可以使用&来获取f2x的地址。


13
个人认为这不仅仅是那样。它说明该参数不可以为空并且是必须的。使用指针时你无法确定,并且必须始终进行检查。而使用引用,函数签名会强制执行程序员的意图,导致更清晰的代码,更准确地反映出意图。 - Len Holgate
1
此外,指针参数可能期望一个数组。引用参数明确地接受一个单一对象,无需进一步的文档说明。 - Mike Seymour

17
在我心中,函数的参数总是按值传递。很容易想象传递一个int类型变量,传递一个double类型变量只是更大一点,而传递一个struct或class可能非常大。但是如果你传递一个指向某个东西的指针,那么你实际上只是通过值传递一个地址(指针通常对于CPU来说大小合适,就像一个int一样)。引用非常相似,当然我认为引用就是指针,只不过具有语法糖,使其看起来像被传递的对象已经按值传递了一样。
你也可以将引用视为常量指针,即:
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,从而导致访问冲突。

在上面的示例中,在调用 foo 时应该先检查指针 p

if (p) foo(*p);

1
这真的非常有帮助,示例也很好。也许您可以在 void foo(int& i); 的例子中添加一个注释,说明使用 NULL 指针调用它会出现什么问题。 - pfabri

14

还有一个未被提及的区别是,你不能改变引用所指向的对象。这在原问题中展示的函数调用示例中并没有太大的区别。

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,无法移动。

引用不能用于像指针这样的简单数组索引。


2

在这两个函数中,你应该能够读取x地址。

f2中,为了这样做,你必须在x前加上一个&,因为在那里,x是一个指向double的引用,而你想要一个地址

引用和指针之间值得注意的一个区别是,前者不能为NULL。当提供一个指针时,必须在文档中说明是否允许/定义了传递NULL。

另一个区别是可读性问题:在可能的情况下使用引用而不是指针可以使代码少些*->,从而使代码更加简洁。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接