按引用传递与按值传递有什么区别?


Answers:


1079

首先,在CS理论中定义的“按值传递与按引用传递”的区别现在已过时,因为最初定义为“按引用传递”的技术已不再受欢迎,并且现在很少使用。1个

较新的语言2倾向于使用不同的(但相似的)技术对来达到相同的效果(见下文),这是造成混淆的主要原因。

造成混淆的第二个原因是,在“通过引用传递”中,“引用”的含义比通用术语“引用”的含义要窄(因为该短语早于它)。


现在,真实的定义是:

  • 通过引用传递参数时,调用者和被调用者对该参数使用相同的变量。如果被调用方修改了参数变量,则效果对调用方的变量可见。

  • 通过value传递参数时,调用方和被调用方将具有两个具有相同值的独立变量。如果被调用方修改了参数变量,则该效果对调用方不可见。

在此定义中要注意的是:

  • 这里的“变量”是指调用方的(本地或全局)变量本身 -即,如果我通过引用传递局部变量并将其分配给它,那么我将更改调用方的变量本身,而不是例如它是指针时指向的变量。

    • 现在,这被视为不好的做法(作为隐式依赖)。因此,几乎所有较新的语言都是专有的或几乎专有的值传递。现在,在函数不能返回多个值的语言中,按引用传递主要以“输出/输入参数”的形式使用。
  • “通过引用传递”中“引用”的含义。与“参考”一词的不同之处在于,该“参考”是临时的和隐含的。被呼叫者基本上得到的是一个“变量”,它在某种程度上与原始变量“相同”。这种效果的具体实现方式是无关紧要的(例如,语言还可以公开一些实现细节-地址,指针,取消引用-都是不相关的;如果最终效果是,则是按引用传递)。


现在,在现代语言中,变量倾向于是“引用类型”(另一种概念是在“按引用传递”之后发明并受其启发),即,实际的对象数据分别存储在某个地方(通常在堆上),并且仅对其的“引用”曾经保存在变量中并作为参数传递。3

传递这样的引用属于传递值,因为从技术上讲变量的值是引用本身,而不是引用的对象。但是,对程序的净影响可以与按值传递或按引用传递相同:

  • 如果参考仅仅是从呼叫者的可变取出并作为参数传递,这具有作为通过按引用相同的效果:如果引用的对象被突变在被叫方,主叫方会看到的变化。
    • 但是,如果重新保存了具有此引用的变量它将停止指向该对象,因此对该变量进行的任何进一步操作都会影响它现在指向的内容。
  • 为了具有与值传递相同的效果,在某个时候制作对象的副本。选项包括:
    • 呼叫者可以在呼叫之前制作一个私有副本,然后给被呼叫者一个引用。
    • 在某些语言中,某些对象类型是“不可变的”:对它们进行的任何更改其值的操作实际上都会创建一个全新的对象,而不会影响原始对象。因此,将这样类型的对象作为参数传递始终具有按值传递的效果:如果需要更改,当需要更改时,将自动为被调用方创建一个副本,并且调用方的对象永远不会受到影响。
      • 在功能语言中,所有对象都是不可变的。

如您所见,这对技术与定义中的技术几乎相同,只是间接的程度:只需将“变量”替换为“引用对象”即可。

它们没有统一的名称,这导致了诸如“按值调用,其中值是引用”的错误解释。1975年,芭芭拉·里斯科夫(Barbara Liskov)提出了“ 按对象共享调用 ”(有时也称为“按共享调用”)一词,尽管该术语从未流行开来。而且,这两个短语都没有与原始短语相似。难怪旧的术语最终会在没有更好的东西的情况下被重用,从而导致混乱。4


注意:很长一段时间,这个答案曾经说过:

假设我想与您共享一个网页。如果我告诉您该URL,那么我是通过引用传递的。您可以使用该URL查看与我看到的网页相同的网页。如果该页面被更改,我们都会看到更改。如果删除该URL,那么您所做的就是破坏对该页面的引用-您并不是在删除实际的页面本身。

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

“引用”的狭义含义,这基本上是正确的-它既是临时的又是隐式的(不是必须的,但显式和/或持久的是附加功能,而不是“按引用传递”语义的一部分) ,如上所述)。更为贴近的类比是给您一份文档副本,而不是邀请您使用原始文档。


1 除非您使用Fortran或Visual Basic进行编程,否则它不是默认行为,并且在现代使用的大多数语言中,甚至不可能进行真正的按引用调用。

2 相当多的老年人也支持它

3 在几种现代语言中,所有类型都是引用类型。这种方法由1975年的CLU语言首创,此后被包括Python和Ruby在内的许多其他语言采用。还有更多的语言使用混合方法,其中某些类型是“值类型”,而其他类型是“引用类型”,其中包括C#,Java和JavaScript。

4 回收合适的旧术语本身并没有什么坏处但是必须以某种方式弄清楚每次使用的含义。不这样做正是造成混乱的原因。


我个人会使用术语“新的”“间接的”按值传递/按引用传递新技术。
ivan_pozdeev,

您提供的“真实的”定义并不是几乎所有编程入门课程都给出的定义。Google通过参考传递了什么,您将不会得到答案。您提供的真实定义是滥用引用这个词,因为当您遵循该定义时,您使用的是别名而不是引用:您有两个实际上是相同变量的变量,即别名而不是引用。您的真实定义无缘无故会造成大量混乱。只是说引用传递就是传递地址。这是有道理的,可以避免这种毫无意义的混乱。
YungGun

@YungGun 1)请提供指向“几乎每门入门编程课程都给出的定义”的链接。还要注意,这样做的目的是要在当今的现实中变得清晰,而不是在十年或三年前编写某些CS课程时的现实中。2)“地址”不能在定义中使用,因为它故意从可能的实现中抽象出来。例如,某些语言(Fortran)没有指针。它们是否向用户公开原始地址也有所不同(VB没有);它也不必是原始内存地址,任何允许链接到变量的东西都可以。
ivan_pozdeev

@ivan_podeev没有链接对不起。我之所以说“几乎所有入门课程”是因为我个人上过大学并且参加了编程训练营,这也教会了我这一点。这些课程是现代的(少于5年前)。“原始地址”是“指针”的同义词...从技术上来说,您可能是正确的(根据某些经挑选的链接),但是对于大多数程序员而言,您使用的语言是不切实际且令人困惑的。如果您想对此进行全面思考,我已写了3500个字的博客文章:medium.com/@isaaccway228/…–
YungGun

@YungGun“太长了,没看过”。一目了然,准确地显示出答案中概述的困惑。通过引用传递是与实现无关的抽象技术。到底传递了什么无关紧要,对程序的影响无关紧要。
ivan_pozdeev,

150

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

  • Java仅支持按值传递。始终复制参数,即使将引用复制到对象时,被调用函数中的参数将指向同一对象,并且在调用者中将看到对该对象的更改。因为这会引起混乱,这里是什么乔恩斯基特不得不说这件事。
  • C#支持按值传递和按引用传递(ref调用方和被调用函数使用的关键字)。乔恩·斯基特(Jon Skeet)在这里对此也有很好的解释。
  • C ++支持按值传递和按引用传递(在调用函数中使用的引用参数类型)。您将在下面找到对此的解释。

代号

由于我的语言是C ++,因此我将在这里使用它

// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
    p = NULL;
}

// passes an integer
void call_by_value(int p) { // :2
    p = 42;
}

// passes an integer by reference
void call_by_reference(int & p) { // :3
    p = 42;
}

// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
    *p = 10; // changes what p points to ("what p references" in java)
    // only changes the value of the parameter, but *not* of 
    // the argument passed by the caller. thus it's pass-by-value:
    p = NULL;
}

int main() {
    int value = 10;
    int * pointer = &value;

    call_by_value(pointer); // :1
    assert(pointer == &value); // pointer was copied

    call_by_value(value); // :2
    assert(value == 10); // value was copied

    call_by_reference(value); // :3
    assert(value == 42); // value was passed by reference

    call_by_value_special(pointer); // :4
    // pointer was copied but what pointer references was changed.
    assert(value == 10 && pointer == &value);
}

用Java编写的示例不会受到伤害:

class Example {
    int value = 0;

    // similar to :4 case in the c++ example
    static void accept_reference(Example e) { // :1
        e.value++; // will change the referenced object
        e = null; // will only change the parameter
    }

    // similar to the :2 case in the c++ example
    static void accept_primitive(int v) { // :2
        v++; // will only change the parameter
    }        

    public static void main(String... args) {
        int value = 0;
        Example ref = new Example(); // reference

        // note what we pass is the reference, not the object. we can't 
        // pass objects. The reference is copied (pass-by-value).
        accept_reference(ref); // :1
        assert ref != null && ref.value == 1;

        // the primitive int variable is copied
        accept_primitive(value); // :2
        assert value == 0;
    }
}

维基百科

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference

这个家伙几乎钉住了它:

http://javadude.com/articles/passbyvalue.htm


9
为什么要下票?如果有任何错误或导致误解,请发表评论。
Johannes Schaub-litb

1
这不是我的投票,而是在一个小问题上(您知道,在总统辩论中使用的那种投票),我说这更多是“战术”而非“战略”。
harpo

28
+1为完整性。不用担心投票不足-人们出于奇怪的原因这么做。在一个关于计算器的观点问题中,每个人都被一个不认为程序员不应该使用计算器的人所打败!无论如何,我认为您的回答非常好。
Mark Brittingham 2009年

1
指向Skeet的说明的链接已断开。
面向货币的程序员,

到Skeet的解释的链接仍然断开。
Rokit

85

实际上,这里的许多答案(尤其是最受好评的答案)都是错误的,因为它们会误解“按引用呼叫”的真正含义。这是我尝试解决的问题。

TL; DR

简单来说:

  • 按值调用意味着您将作为函数参数传递
  • 通过引用调用意味着您将变量作为函数参数传递

用隐喻的术语来说:

  • 价值召唤我在纸上写下一些东西交给你的地方。也许这是一个URL,也许是《战争与和平》的完整副本。不管它是什么,它都在我给您的一张纸上,所以现在它实际上就是您的纸了。您现在可以在那张纸上随意涂鸦,或者使用那张纸在其他地方找到东西并随便摆弄。
  • 我给你我的笔记本上写下东西,称为参考致电。您可以在我的笔记本上涂鸦(也许我想要您,也许我不想),然后我随您放置的所有涂鸦一起保存我的笔记本。另外,如果您或我所写的内容包含有关如何在其他地方找到东西的信息,则您或我都可以去那里弄弄这些信息。

“按价值调用”和“按引用调用” 不是什么意思

请注意,这两个概念与引用类型(在Java中是的子类型Object,在C#中是所有class类型的所有类型)或指针类型(在C中的指针类型)的概念完全独立并且正交。Java的“引用类型”,只是使用不同的语法)。

引用类型的概念对应于一个URL:它本身既是一条信息,又是对其他信息的引用(如果有的话,是一个指针)。您可以在不同的地方拥有许多URL副本,它们不会更改它们都链接到的网站;如果网站已更新,则每个URL副本仍将导致更新的信息。相反,在任何地方更改URL都不会影响URL的任何其他书面副本。

需要注意的是C ++有“参考”(例如的概念int&),是不是像Java和C#的‘引用类型’,但就是像‘通过引用调用’。Java和C#的“引用类型”以及Python中的所有类型都类似于C和C ++称为“指针类型”(例如int*)。


好的,这是更长更正式的解释。

术语

首先,我想强调一些重要的术语,以帮助阐明我的答案,并确保我们在使用单词时都引用相同的想法。(实际上,我认为,对于此类主题的绝大多数困惑源于使用单词的方式无法充分传达所要表达的含义。)

首先,这是一个类似C语言的函数声明示例:

void foo(int param) {  // line 1
  param += 1;
}

这是调用此函数的示例:

void bar() {
  int arg = 1;  // line 2
  foo(arg);     // line 3
}

使用此示例,我想定义一些重要的术语:

  • foo是在第1行上声明的函数(Java坚持要使用所有函数方法,但是概念是相同的,并且不会失去一般性; C和C ++在声明和定义之间进行区分,我将不在这里介绍)
  • param是一个正式的参数foo,还宣布1号线
  • arg变量,特别是函数的局部变量bar在第2行上进行了声明和初始化
  • arg也是一个参数到特定的调用foo第3行

这里要区分两个非常重要的概念。首先是变量

  • 计算表达式的结果的语言。例如,在bar上面的函数中,该行int arg = 1;之后的表达式arg具有 1
  • 变量是一个对值的容器。变量可以是可变的(这是大多数类似C的语言中的默认设置),只读(例如,使用Java final或C#声明readonly)或深度不可变(例如,使用C ++ const)。

要区分的另一对重要概念是参数参数

  • 参数(也称为形式参数)是可变的,其必须由呼叫者调用函数时被供应。
  • 一个参数是一个,其由函数的调用者提供满足该功能的具体形式参数

按价值致电

按值调用中,函数的形式参数是为函数调用新创建的变量,并使用其参数的进行初始化。

这与使用值初始化任何其他种类的变量的方式完全相同。例如:

int arg = 1;
int another_variable = arg;

这里arganother_variable是完全独立的变量-它们的值可以彼此独立地更改。但是,在another_variable声明的位置,将其初始化为拥有与相同的值,arg1

由于它们是自变量,因此更改another_variable不会影响arg

int arg = 1;
int another_variable = arg;
another_variable = 2;

assert arg == 1; // true
assert another_variable == 2; // true

argparam上面的示例中和之间的关系完全相同,为对称起见,我将在此处重复:

void foo(int param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

就像我们以这种方式编写代码一样:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here

也就是说,按值调用意味着什么的定义特征是,被调用方(foo在这种情况下)接收作为参数,但是从调用方的变量(在这种情况下)具有自己的变量bar

回到上面的隐喻,如果我bar和你是foo,当我打电话给你时,我会给你一张纸,上面写着一个价值。你叫那张纸param。该值是我在笔记本中写的值(我的局部变量)的副本,该变量称为arg

(顺便说一句:取决于硬件和操作系统,关于如何从另一个函数调用一个函数有多种调用约定。该调用约定就像我们决定是否将值写在纸上然后交给您,或者如果您有一张纸写在上面,或者如果我把它写在我们俩面前的墙上。这也是一个有趣的话题,但远远超出了已经很长的答案的范围。)

通过参考电话

按引用调用中,函数的形式参数只是调用者作为参数提供的相同变量的新名称

回到上面的示例,它等效于:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here

由于param只是-的另一个名称,arg也就是说,它们是相同的变量,因此对的更改param会反映在中arg。这是按引用调用与按值调用不同的基本方式。

很少有语言支持按引用调用,但是C ++可以做到这一点:

void foo(int& param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

在这种情况下,param不只是具有相同价值arg,它实际上 arg(只是一个不同的名称)等bar可以观察到,arg已递增。

请注意,这不是 Java,JavaScript,C,Objective-C,Python或当今几乎所有其他流行语言的工作方式。这意味着这些语言不是按引用调用的,而是按值调用的。

附录:通过对象共享调用

如果您拥有的是按值调用,但实际值是引用类型指针类型,则“值”本身不是很有趣(例如,在C中,它只是平台特定大小的整数)-什么有趣的是价值指向的是什么。

如果该引用类型(即指针)指向的变量是可变的,则可能会产生有趣的效果:您可以修改指向的值,并且调用者可以观察到指向值的更改,即使调用者无法观察到更改为指针本身。

再次借用URL的类比,如果我们俩都关心的是网站而不是URL,那么我给您提供URL 副本到网站这一事实并不是特别有趣。您对URL副本进行涂鸦不会影响我的URL副本,这并不是我们关心的事情(事实上,在Java和Python等语言中,“ URL”或引用类型值可以根本无法修改,只有它所指向的东西才能被修改)。

当Barbara Liskov发明CLU编程语言(具有这些语义)时,她意识到现有的术语“按值调用”和“按引用调用”对于描述这种新语言的语义并不是特别有用。因此,她发明了一个新术语:通过对象共享进行调用

在讨论从技术上讲是按值调用的语言,但在使用的通用类型是引用或指针类型的语言(即:几乎每种现代命令式,面向对象或多范式编程语言)时,我发现它的混淆要少得多只需避免谈论按值调用按引用调用。坚持通过对象共享进行调用(或简单地通过object进行调用),没有人会感到困惑。:-)


更好地解释:这里有两个非常重要的概念需要区分。 The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
SK Venkat

2
极好的答案。我想补充一点是,无需通过引用来创建新的存储。参数名称引用了原始存储(内存)
谢谢

1
IMO的最佳答案
Rafael Eyng

59

在理解这两个术语之前,您必须了解以下内容。每个对象都有两个可以区分的事物。

  • 它的价值。
  • 它的地址。

所以如果你说 employee.name = "John"

我知道关于有两件事name。它的值,它"John"同时也是它在这也许是一些像这样的十六进制数的存储位置:0x7fd5d258dd00

根据语言的体系结构或对象的类型(类,结构等),您将转移"John"0x7fd5d258dd00

传递"John"称为按值传递。通过0x7fd5d258dd00称为通过引用传递。指向此存储位置的任何人都可以访问的值"John"

有关此的更多信息,我建议您阅读有关取消引用指针的知识,以及为什么选择结构(值类型)而不是类(引用类型)


3
那是我一直在寻找的东西,实际上是一个人们应该寻找的概念,而不仅仅是解释,竖起大拇指。
Haisum Usman'1

Java总是按值传递。在Java中传递对对象的引用被视为按值传递。这与您的陈述“传递0x7fd5d258dd00被称为通过引用传递”相矛盾。
chetan raina

53

这是一个例子:

#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
我认为存在一个问题,因为最后一行应打印0而不是2。请告诉我是否缺少某些内容。
Taimoor Changaiz 2014年

@TaimoorChangaiz; 哪个“最后一行”?顺便说一句,如果可以使用IRC,请在Freenode上进行## programming。在那里解释事情会容易得多。我的昵称是“ pyon”。
2014年

1
@EduardoLeónby_val(y); std :: cout << y << std :: endl; //打印2
Taimoor Changaiz 2014年

5
@TaimoorChangaiz:为什么不打印2?y已由上一行设置为2。为什么会回到0?
2014年

@EduardoLeón,我不好。是的,你是对的。感谢您的指正
Taimoor Changaiz 2014年

28

最简单的方法是在Excel文件中。举例来说,假设您在A1和B1单元格中有两个数字5和2,而您想在第三个单元格中找到它们的总和,例如A2。您可以通过两种方式执行此操作。

  • 通过在此单元格中键入= 5 + 2 将其值传递到单元格A2中。在这种情况下,如果单元格A1或B1的值发生变化,则A2中的总和保持不变。

  • 或者通过键入= A1 + B1 将单元格A1和B1的“引用”传递到单元格A2。在这种情况下,如果单元格A1或B1的值更改,则A2中的总和也更改。


在所有其他答案中,这是最简单,最好的示例。
阿米特·雷

18

当通过ref传递时,您基本上是传递一个指向变量的指针。通过值传递,您正在传递变量的副本。在基本用法中,这通常意味着对变量的按引用传递更改将被视为调用方法,而按值传递则不会。


12

按值传递将存储在您指定的变量中的数据的COPY,按引用传递将直接链接到变量本身。因此,如果通过引用传递变量,然后在将其传递到的块中更改该变量,则原始变量将被更改。如果仅按值传递,则传递给它的块将无法更改原始变量,但是您将获得调用时包含的任何内容的副本。


7

按值传递-该函数复制变量并使用副本(因此它不会更改原始变量中的任何内容)

通过引用传递-该函数使用原始变量,如果您在另一个函数中更改了该变量,它也会在原始变量中也进行更改。

示例(复制并自己使用/尝试一下):

#include <iostream>

using namespace std;

void funct1(int a){ //pass-by-value
    a = 6; //now "a" is 6 only in funct1, but not in main or anywhere else
}
void funct2(int &a){ //pass-by-reference
    a = 7; //now "a" is 7 both in funct2, main and everywhere else it'll be used
}

int main()
{
    int a = 5;

    funct1(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 5
    funct2(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 7

    return 0;
}

保持简单,偷看。文字墙可能是个坏习惯。


确实对了解参数值是否已更改非常有帮助,谢谢!
Kevin Zhao)

5

它们之间的主要区别在于,值类型变量存储值,因此在方法调用中指定值类型变量会将该变量值的副本传递给方法。引用类型变量存储对对象的引用,因此将引用类型变量指定为参数将使该方法将引用对象的实际引用的副本传递给该方法。即使引用本身是按值传递的,该方法仍可以使用接收到的引用与原始对象进行交互,并可能对其进行修改。类似地,当通过return语句从方法返回信息时,该方法返回存储在value-type变量中的值的副本或存储在reference-type变量中的引用的副本。当返回引用时,调用方法可以使用该引用与引用的对象进行交互。所以,

在c#中,要通过引用传递变量,以便被调用的方法可以修改变量的值,C#提供了关键字ref和out。将ref关键字应用于参数声明使您可以通过引用将变量传递给方法-被调用的方法将能够在调用方中修改原始变量。ref关键字用于在调用方法中已初始化的变量。通常,当方法调用包含未初始化的变量作为参数时,编译器会生成错误。在带有关键字out的参数之前创建一个输出参数。这向编译器指示该参数将通过引用传递给被调用的方法,并且被调用的方法将为调用方中的原始变量分配一个值。如果该方法未在每个可能的执行路径中为输出参数分配值,则编译器将生成错误。这也可以防止编译器为未初始化的变量生成错误消息,该错误消息作为参数传递给方法。一个方法只能通过return语句将一个值返回给其调用方,但是可以通过指定多个输出(ref和/或out)参数来返回许多值。

请参阅c#讨论和示例,在此处链接文本


3

例子:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

const &通常是最好的。您不会招致建设和破坏的罚款。如果引用不是const,则您的界面建议它将更改传入的数据。


2

简而言之,按值传递是什么,按引用传递是在哪里。

如果您的值为VAR1 =“ Happy Guy!”,则只会看到“ Happy Guy!”。如果VAR1更改为“ Happy Gal!”,您将不会知道。如果通过引用传递了它,并且VAR1发生了变化,那么您将。


2

如果您不希望在将原始变量的值传递给函数后更改其值,则应使用“ 按值传递 ”参数来构造该函数。

然后,该函数将仅具有值,而不具有传入变量的地址。没有变量的地址,函数内部的代码就无法更改变量值,如从函数外部看到的那样。

但是,如果要使该函数具有更改变量值(从外部观察)的能力,则需要使用“ 按引用传递”。因为值和地址(引用)都传入并且在函数内部可用。


1

按值传递是指如何通过使用参数将值传递给函数。在按值传递时,我们复制存储在指定变量中的数据,这比复制数据要慢于通过引用传递数据。我们对复制的数据进行更改,原始数据不受影响。通过引用或地址传递,我们将直接链接发送到变量本身。或将指针传递给变量。它更快,花费更少的时间


0

这是一个示例,演示按值传递-指针值-引用之间的区别:

void swap_by_value(int a, int b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}   
void swap_by_pointer(int *a, int *b){
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}    
void swap_by_reference(int &a, int &b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main(void){
    int arg1 = 1, arg2 = 2;

    swap_by_value(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 1 2

    swap_by_pointer(&arg1, &arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1

    arg1 = 1;                               //reset values
    arg2 = 2;
    swap_by_reference(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1
}

“通过引用”方法具有重要的局限性。如果一个参数被声明为通过引用传递(因此它前面带有&符号),则其对应的实际参数必须是一个变量

引用“按值传递”形式参数的实际参数可以是表达式通常,因此,它不仅可以使用变量,还可以使用文字或函数调用的结果。

该函数不能将值放在变量以外的其他值中。它不能为文字分配新值,也不能强制表达式更改其结果。

PS:您也可以在当前主题中检查Dylan Beattie答案,以简单的语言对其进行解释。


您声明“如果声明一个参数(作为引用),则其相应的实际参数必须是变量”,但通常情况并非如此。如果引用绑定到临时对象(例如函数的返回值),则会延长其生存期以匹配该引用。有关详细信息,请参见此处
克里斯·亨特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.