3

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

void swap(int &x, int &y)

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

void swap(int* x, int* y)

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

4

4 回答 4

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
}
于 2015-01-10T17:34:46.663 回答
4

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

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

引用最终成为其他值的别名。实现它们的最常见方法是作为引擎盖下的指针:但由于无法访问该指针值,也没有通过它访问的副作用,这使得编译器很容易完全消除引用的存在, 并且直接使用引用的值。

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

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

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

于 2015-01-10T18:42:57.627 回答
3

两个要点:

  • C中没有引用调用。
  • 按值传递和按引用传递是不同的。他们不一样。

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

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

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

在传递对原始变量的引用/指针时,实际上对象/地址是按值传递的。因此,您可以说按引用传递是按值传递引用,但这并不意味着按引用传递是按值传递的伪名称。这个答案很好地解释了两者之间的区别。我正在复制摘录:

如果我告诉你 URL,我是通过引用传递的。您可以使用该 URL 来查看我可以看到的同一网页。如果该页面发生更改,我们都会看到更改。如果您删除 URL,您所做的只是破坏您对该页面的引用 - 您并没有删除实际页面本身。

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

于 2015-01-10T17:33:38.890 回答
1

你不能说“按引用传递”实际上是“按值传递引用”而不给出“引用”的定义。

Java中,该语句是正确的(最初近似),因为在函数内部,您可以更改作为引用的形式参数的值,而无需更改在调用中传递的参数的值:

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

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

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

C++中,这是非常不同的。当你在函数内部更改引用参数的值时,调用中传递的参数被修改:

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

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

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

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

于 2015-01-10T19:00:20.937 回答