为什么程序员会说"通过引用传递"真的"通过值传递引用?"为什么这很重要?

时间:2015-01-10 17:30:44

标签: java c++ c pass-by-reference call-by-value

我知道在C& amp;中通过引用传递的整个概念。 C ++,以及在Java中仅通过值传递的类似概念。 但从一个角度看,一切都是通过价值不是吗? 在C中,我们将变量的指针传递给函数。所以我们只是将参考值传递给函数。这就是我们说Java不支持通过引用传递的原因,因为我们只是将引用变量的值传递给函数。所以我们按值传递引用。虽然在C ++中有一种通过引用传递的方法,因为我们可以传递参数,并且该函数将使用这种格式使用相同的内存位置

void swap(int &x, int &y)

但是通过C中的指针引用传递只是按值传递指针。

void swap(int* x, int* y)

我知道这个问题可能看起来有点愚蠢,但我觉得整个概念中存在一个巨大的漏洞。那么通过引用调用的实际定义是什么,这只是在另一个上下文中按值调用的pseduo名称?

4 个答案:

答案 0 :(得分:4)

通过引用传递意味着被调用函数的参数将与调用者的传递参数相同(不是值,而是标识 - 变量本身)。按值传递意味着被调用函数的参数将是调用者传递参数的副本。值将是相同的,但身份 - 变量 - 是不同的。因此,在一种情况下由被调用函数完成的参数更改会更改传递的参数,而在另一种情况下,只更改被调用函数中的参数值(这只是一个副本)。

C ++中的简单示例

#include <iostream>

void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }

int main()
{
    int x = 0;
    by_val(x); std::cout << x << std::endl;  // prints 0
    by_ref(x); std::cout << x << std::endl;  // prints 2

    int y = 0;
    by_ref(y); std::cout << y << std::endl;  // prints 2
    by_val(y); std::cout << y << std::endl;  // prints 2
}

答案 1 :(得分:3)

两个要点:

  • C中没有参考号召。
  • 按值传递并按引用传递是不同的。它们不一样。

按值传递:被调用函数在堆栈中创建一组新变量,并将参数值复制到其中。

通过引用传递:而不是将值传递给被调用的函数,传递对原始变量的引用/指针。

  

为什么程序员说“通过引用传递”实际上是“通过值传递引用?”

在传递对原始变量的引用/指针时,实际上对象/地址是按值传递的。所以,您可以说通过引用传递按值传递引用但这并不意味着通过引用传递传递值的伪名称。在this answer中很好地解释了两者之间的差异。我正在复制摘录:

  

如果我告诉您网址,我通过引用传递。您可以使用该网址查看我能看到的相同网页。如果该页面已更改,我们都会看到更改。如果您删除了网址,那么您所做的就是销毁对该网页的引用 - 您不会删除实际的网页本身。

     

如果我打印出页面并给你打印输出,我通过值。您的页面是原始的断开连接的副本。您不会看到任何后续更改,并且您所做的任何更改(例如,在您的打印输出上涂鸦)都不会显示在原始页面上。如果你破坏了打印输出,你实际上已经销毁了对象的你的副本 - 但是原始网页仍然完好无损。

答案 2 :(得分:3)

C ++由标准的模数错误定义。 C ++标准没有指定如何实现引用,也没有指定它们如何传递给函数。它也不是一般定义调用约定,也不定义指针布局,堆栈,堆或无数其他实现细节。

相反,它试图定义C ++代码的含义。

引用最终成为其他值的别名。实现它们的最常见方式是作为引擎盖下的指针:但由于无法访问该指针值,也无法通过它访问它的副作用,因此编译器可以轻松地完全消除引用的存在,并直接使用引用的值。

如果编译器不能这样做,它通常会传递指针或等效物。

尽管经常被实现为指针,但它仍然不同,因为与它的交互的语义是不同的:它们不能被重新安置,它们不能获取它们的地址,并且它们不能是未初始化的(null或等效的)。

当然,这些规则可以通过未定义的行为来打破。

答案 3 :(得分:1)

你不能说&#34;通过引用&#34;是真的&#34;通过价值传递参考&#34;没有给出&#34;参考&#34;的定义。

Java 中,该语句为true(最初约为),因为在函数内部,您可以更改作为引用的形式参数的值,而不更改传递的参数值。打电话:

void f(Object param) {
  param = null;
}

void g() {
  Object o = new Object();
  System.out.println(o);
  f(o);
  System.out.printn(o);
}

每个人都知道这两个印刷语句会给出完全相同的结果。实际上 in Java 没有通过引用传递,只有可以传递的引用;并且只有一种方法可以传递参数:按值。

C ++ 中,这确实不同。更改函数内参考参数的值时,将修改调用中传递的参数:

void f(int &param) {
  param = 0;
}

void g() {
  int i=12;
  cout << i << endl;
  f(i);
  cout << i << endl;
}

每个C ++程序员都知道第二个打印将显示0(而第一个是12)。

所以在C ++中,你不能说&#34;通过引用传递&#34;是&#34;通过值&#34;传递引用。在C ++引用中没有值,它们只是与内存块相关联的名称(标准部分8.3.2参考:[注意:引用可以被认为是对象的名称。 - 结束注释] )。在f()内,只有一个变量有两个名字。当然,您可能会反对大多数C ++引用实现都是隐藏指针,因此该语句可以为true;但是你应该知道,在很多情况下可以避免使用隐藏指针来实现引用(标准部分8.3.2 / 7引用。未指定引用是否需要存储)。所以在C ++中声明无效。 C ++实际上有两种传递参数的方法:按值和按引用。