我知道C和C++中通过引用传递的整个概念,以及在Java中仅按值传递的类似概念。但从某种角度来看,一切都是按价值传递的,不是吗?在 C 中,我们将变量的指针传递给函数。所以我们只是将引用的值传递给函数。这就是我们说Java不支持按引用传递的原因,因为我们只是将引用变量的值传递给函数。所以我们按值传递引用。虽然在C++有一种通过引用传递的方法,因为我们可以传递参数,并且该函数将使用这种格式使用相同的内存位置
void swap(int &x, int &y)
但是在 C 中通过指针通过引用传递只是按值传递指针。
void swap(int* x, int* y)
我知道这个问题可能看起来有点愚蠢,但我觉得我的整个概念中有一个很大的漏洞。那么按引用调用的实际定义是什么,这只是另一个上下文中按值调用的别名吗?
通过引用传递意味着被调用函数的参数将与调用方传递的参数相同(不是值,而是标识 - 变量本身)。按值传递意味着被调用函数的参数将是调用方传递的参数的副本。值将相同,但标识 - 变量 - 不同。因此,在一种情况下,被调用函数对参数的更改会更改传递的参数,而在另一种情况下,只是更改被调用函数中参数的值(这只是一个副本)。
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
}
C++由标准的模错误定义。 C++ 标准没有指定如何实现引用,也没有指定如何将它们传递给函数。 它也没有定义一般的调用约定,也没有定义指针布局、堆栈、堆或无数其他实现细节。
相反,它试图定义C++代码的含义。
引用最终成为其他值的别名。 实现它们的最常见方法是作为底层指针:但由于无法访问该指针值,也无法通过它访问的副作用,这使得编译器很容易完全消除引用的存在,而只是直接使用引用的值。
如果编译器无法执行此操作,它通常会传递指针或等效项。
尽管经常被实现为指针,但它仍然不同,因为与它交互的语义是不同的:它们不能被重新拔插,它们不能被占用它们的地址,并且它们不能被取消初始化(null 或等效)。
当然,这些规则可以通过未定义的行为来打破。
两个要点:
- 在 C 中没有通过引用调用。
- 按值传递和按引用传递是不同的。它们不一样。
按值传递:被调用的函数在堆栈中创建一组新的变量,并将参数的值复制到其中。
通过引用传递:不是将值传递给被调用的函数,而是传递对原始变量的引用/指针。
为什么程序员说"按引用传递"实际上是"按值传递引用"?
在传递指向原始变量的引用/指针时,实际上对象/地址是按值传递的。因此,您可以说按引用传递是按值传递引用,但这并不意味着按引用传递是按值传递的伪名称。两者之间的区别在这个答案中得到了很好的解释。我正在复制摘录:
如果我告诉你网址,我就是通过引用传递。您可以使用该URL查看我可以看到的同一网页。如果该页面发生更改,我们都会看到更改。如果您删除 URL,您所做的只是销毁对该页面的引用 - 您不会删除实际页面本身。
如果我打印出页面并给你打印输出,我就是通过价值传递。您的页面是原始页面的断开连接的副本。您不会看到任何后续更改,并且您所做的任何更改(例如在打印输出上涂鸦)都不会显示在原始页面上。如果您销毁打印输出,您实际上已经销毁了对象的副本 - 但原始网页保持不变。
如果不给出"引用"的定义,就不能说"按引用传递"实际上是"按值传递引用"。
在 Java 中,该语句为 true(起初大约为真),因为在函数中,您可以更改作为引用的形式参数的值,而无需更改调用中传递的参数的值:
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 ¶m) {
param = 0;
}
void g() {
int i=12;
cout << i << endl;
f(i);
cout << i << endl;
}
每个C++程序员都知道第二次打印将显示 0(而第一次打印为 12)。
因此,在C++中,您不能说"按引用传递"是"按值传递引用"。在C++引用没有价值,它们只是与内存块相关联的名称(标准部分 8.3.2 引用:[ 注意:引用可以被认为是对象的名称。在f()
内部只有一个变量有两个名称。当然,您可能会反对大多数引用实现都是隐藏指针C++因此该语句可能是真的;但您应该知道,在许多情况下,可以避免使用隐藏指针来实现引用(标准部分 8.3.2/7 引用。未指定引用是否需要存储)...因此,在C++中,该声明无效。C++实际上有两种传递参数的方式:按值传递和按引用传递参数。