C和C++开发人员都使用短语“通过引用传递”,但是它们似乎用来表示不同的含义。每种语言中这个模棱两可的词组之间到底有什么区别?
最佳答案
有些问题已经解决了the difference between passing by reference and passing by value。从本质上讲,按值将参数传递给函数意味着函数将拥有其自己的参数副本-将其值复制。修改该副本不会修改原始对象。但是,通过引用传递时,函数内部的参数引用的是传入的同一对象-函数内部的任何更改都可以在外部看到。
不幸的是,有两种使用短语“按值传递”和“按引用传递”的方法,这会引起混淆。我认为,这部分是为什么新C++程序员难以采用指针和引用的原因,尤其是当它们来自C的背景时。
C
在C语言中,技术意义上的一切都是通过值(value)传递的。也就是说,无论您将什么作为函数的参数,它都会被复制到该函数中。例如,使用void foo(int)
调用函数foo(x)
会将x
的值复制为foo
的参数。可以在一个简单的示例中看到:
void foo(int param) { param++; }
int main()
{
int x = 5;
foo(x);
printf("%d\n",x); // x == 5
}
x
的值被复制到foo
中,并且该副本递增。 x
中的main
继续具有其原始值。如您所知,对象可以是指针类型。例如,
int* p
将p
定义为指向int
的指针。请务必注意,以下代码引入了两个对象:int x = 5;
int* p = &x;
第一个是
int
类型,值5
。第二个是int*
类型,其值是第一个对象的地址。当传递指向函数的指针时,您仍在按值传递它。它包含的地址被复制到函数中。修改函数内部的指针不会更改函数外部的指针-但是,修改其指向的对象将更改函数外部的对象。但为什么?
由于具有相同值的两个指针总是指向相同的对象(它们包含相同的地址),因此所指向的对象可以通过两者来访问和修改。这给出了通过引用传递指向对象的语义,尽管实际上实际上不存在引用-C语言中根本没有引用。请看一下经过更改的示例:
void foo(int* param) { (*param)++; }
int main()
{
int x = 5;
foo(&x);
printf("%d\n",x); // x == 6
}
我们可以说在将
int*
传递给函数时,它指向的int
是“通过引用传递”的,但实际上int
从未真正传递给任何地方-仅将指针复制到函数中。这给我们提供了“按值传递”和“按引用传递”的口语含义。该术语的用法由标准内的术语支持。当您具有指针类型时,它所指向的类型称为其引用类型。也就是说,
int*
的引用类型是int
。A pointer type may be derived from a function type, an object type, or an incomplete type, called the referenced type.
虽然一元
*
运算符(如*p
中的)在标准中被称为间接寻址,但通常也被称为对指针的解引用。这进一步促进了C语言中“通过引用传递”的概念。C++
C++从C采用了许多其原始语言功能。其中包括指针,因此这种“按引用传递”的通俗形式仍然可以使用-
*p
仍在取消引用p
。但是,使用该术语会造成混淆,因为C++引入了C所没有的功能:真正传递引用的能力。后面跟有“&”号的类型是引用类型2。例如,
int&
是对int
的引用。当将参数传递给采用引用类型的函数时,该对象实际上是通过引用传递的。没有指针,没有对象的复制,没有任何东西。函数内部的名称实际上指的是与传入的对象完全相同的对象。与上面的示例形成对比:void foo(int& param) { param++; }
int main()
{
int x = 5;
foo(x);
std::cout << x << std::endl; // x == 6
}
现在,
foo
函数具有一个对int
的引用的参数。现在,当传递x
时,param
指向完全相同的对象。递增param
可以使x
的值发生明显变化,现在x
的值可以为6。在此示例中,没有任何值传递。没有复制任何内容。与C中通过引用传递实际上只是按值传递指针不同,在C++中,我们可以真正地按引用传递。
由于术语“按引用传递”可能存在歧义,因此在使用引用类型时,最好仅在C++上下文中使用它。如果要传递指针,则不是按引用传递,而是按值传递指针(当然,除非传递对指针的引用!例如
int*&
)。但是,在使用指针时,您可能会遇到“按引用传递”的用法,但是现在至少您知道真正发生了什么。其他语言
其他编程语言进一步使事情复杂化。在某些语言(例如Java)中,您拥有的每个变量都称为对对象的引用(与C++中的引用不同,更像是指针),但是这些引用是通过值传递的。因此,即使您似乎正在通过引用传递给函数,您实际上所做的是按值将引用复制到函数中。将新对象分配给传入的引用时,会注意到与C++中按引用传递的细微差别:
public void foo(Bar param) {
param.something();
param = new Bar();
}
如果要使用Java调用此函数,并传入
Bar
类型的某个对象,则将在传入的同一对象上调用param.something()
的调用。这是因为传入了对对象的引用。但是,即使将新的Bar
分配给param
,该函数之外的对象仍然是相同的旧对象。从外面看不到新的。这是因为foo
中的引用已重新分配给新对象。对于C++引用,这种重新分配引用是不可能的。1所谓“口语化”,并不是要暗示“通过引用传递”的C含义不如C++含义真实,只是C++确实具有引用类型,因此您真正是通过引用传递。 C的含义是对值(value)传递的抽象。
2当然,这些是左值引用,现在在C++ 11中我们也有右值引用。
关于c++ - 在C和C++中 “pass by reference”之间到底有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13654138/