在C和C ++中,“通过引用传递”到底有什么区别?

时间:2012-11-30 22:30:43

标签: c++ c terminology pass-by-reference pass-by-value

C和C ++开发人员使用“通过引用传递”这一短语,但它们似乎用于表示不同的东西。每种语言中这个模棱两可的短语究竟有什么区别?

1 个答案:

答案 0 :(得分:53)

有些问题已经涉及the difference between passing by reference and passing by value。实质上,通过值将参数传递给函数意味着函数将拥有自己的参数副本 - 复制其。修改该副本不会修改原始对象。但是,当通过引用传递时,函数中的参数引用传入的相同对象 - 函数内部的任何更改都将在外部看到。

不幸的是,有两种方式可以使短语"通过值"并且"通过引用传递"使用可能导致混淆。我相信这也是新C ++程序员难以采用指针和引用的部分原因,尤其是当他们来自C语言的背景时。

C

在C中,一切都是从技术意义上的价值传递的。也就是说,无论您作为函数的参数提供什么,它都将被复制到该函数中。例如,使用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* pp定义为指向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实际上从未实际传递过 - 只有指针被复制到函数中。这给了我们口语 1 的含义"通过值"并且"通过引用传递"。

该术语的使用由标准中的术语支持。当您有指针类型时,它指向的类型称为引用类型。也就是说,引用的int*类型为int

  

指针类型可以从函数类型,对象类型或不完整中派生   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指的是完全相同的对象。增量paramx的值有明显变化,现在x的值为6。

在此示例中,没有任何值按值传递。什么都没复制。与C不同,通过引用传递实际上只是按值传递指针,在C ++中我们可以通过引用真正传递。

由于术语中的这种潜在歧义&#34;通过引用传递&#34;,当您使用引用类型时,最好只在C ++的上下文中使用它。如果要传递指针,则不是通过引用传递,而是按值传递指针(当然,除非您将引用传递给指针,例如int*&)。但是,您可能会遇到&#34;通过引用传递&#34;当指针被使用时,但现在至少你知道真正发生了什么。


其他语言

其他编程语言使事情变得更复杂。在某些(例如Java)中,您拥有的每个变量都被称为对象的引用(与C ++中的引用不同,更像是指针),但这些引用是按值传递的。因此,即使您似乎通过引用传递给函数,您实际上正在做的是按值将引用复制到函数中。当您将新对象分配给传入的引用时,会注意到在C ++中通过引用传递的这种细微差别:

public void foo(Bar param) {
  param.something();
  param = new Bar();
}

如果你在Java中调用这个函数,传入一些Bar类型的对象,那么对你传入的同一个对象调用param.something()。这是因为你传入了对您的对象的引用。但是,即使为Bar分配了新的param,该函数外部的对象仍然是同一个旧对象。从外面看不到新的。这是因为foo内的引用正被重新分配给新对象。使用C ++引用时,这种重新分配引用是不可能的。


1 通过&#34;口语&#34;,我不是故意建议&#34;的C含义通过引用&#34;没有比C ++更真实的含义,只是C ++确实有引用类型,所以你真正通过引用。 C意义是对真正通过价值的东西的抽象。

2 当然,这些是左值引用,我们现在在C ++ 11中也有rvalue引用。