提问者:小点点

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


两者之间有什么区别

  1. 引用传递的参数
  2. 通过值传递的参数?

你能给我举几个例子吗?


共1个答案

匿名用户

首先,在CS理论中定义的“按值传递与按引用传递”的区别现在已经过时,因为最初定义为“按引用传递”的技术已经失宠,现在很少使用。1

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

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

现在,真正的定义是:

>

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

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

    在这个定义中需要注意的是:

    >

  • “variable”在这里表示调用方的(局部或全局)变量本身--即,如果我通过引用传递一个局部变量并赋值给它,我将更改调用方的变量本身,而不是例如,如果它是指针,则更改它所指向的任何东西。

    • 这现在被认为是坏的实践(作为隐式依赖项)。因此,几乎所有较新的语言都是完全的,或者几乎完全的通过值传递的。在函数不能返回多个值的语言中,按引用传递现在主要以“output/inout参数”的形式使用。

    “比照而过”中“比照”的含义。与一般的“指代”术语不同的是,这个“指代”是暂时的、隐含的。被调用者基本上得到的是一个“变量”,它与原始变量有某种“相同”。具体如何实现这种效果是不相关的(例如,该语言还可能公开一些实现细节--地址、指针、取消引用--这都是不相关的;如果净效果是这样的话,它就是按引用传递)。

    现在,在现代语言中,变量倾向于“引用类型”(另一个比“按引用传递”更晚的概念,并受其启发),即实际对象数据被单独存储在某个地方(通常在堆上),并且只有对它的“引用”才会保存在变量中并作为参数传递。3

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

    • 如果引用只是取自调用方的变量并作为参数传递,这与按引用传递的效果相同:如果引用的对象在被调用方中发生了变化,调用方将看到变化。
      • 但是,如果持有此引用的变量被重新分配,它将停止指向该对象,因此对此变量的任何进一步操作都将影响它现在所指向的内容。
      • 主叫方只需在呼叫之前制作一份私有副本,然后向被呼叫方提供对该副本的引用。
      • 在某些语言中,某些对象类型是“不可变的”:对它们的任何操作似乎改变了值,实际上创建了一个全新的对象,而不影响原来的对象。因此,将这种类型的对象作为参数传递总是具有按值传递的效果:当需要更改时,将自动为被调用方创建一个副本,而调用方的对象将永远不会受到影响。
        • 在函数式语言中,所有对象都是不可变的。

        正如您可能看到的,这对技术几乎与定义中的技术相同,只是有一个间接级别:只需将“变量”替换为“引用对象”。

        它们没有一个一致的名称,这导致了扭曲的解释,比如“在值是引用的情况下按值调用”。1975年,Barbara Liskov提出了“按对象调用共享”这个术语(或者有时只是“按共享调用”),尽管它从未流行起来。此外,这两个词组都不与原词组平行。难怪旧的术语在没有更好的东西的情况下被重复使用,导致混乱。4

        注:很长一段时间以来,这个答案习惯说:

        说我想和你分享一个网页。如果我告诉你网址,我是通过引用。您可以使用该URL来查看与我所看到的相同的网页。如果那一页被改变了,我们都看到了改变。如果您删除了URL,您所做的只是破坏了对该页面的引用--您并不是删除了实际的页面本身。

        如果我打印出这一页并给你打印出来的,我就是在传递值。您的页面是原始页面的断开副本。您不会看到任何后续更改,并且您所做的任何更改(例如,在打印输出上涂鸦)都不会显示在原始页面上。如果您销毁了打印输出,那么实际上您已经销毁了对象的副本--但原始网页仍然保持完整。

        这基本上是正确的,但“引用”的狭义含义除外--它既是临时的,也是隐式的(它不是必须的,但是显式和/或持久化是附加的特性,不是前面解释的通过引用语义的一部分)。一个更接近的类比就是给你一份文档的副本,而不是邀请你去做文档的原件。

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

        2相当多的老版本也支持它

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

        4重复使用一个合适的旧术语本身并没有什么不好,但是你必须弄清楚每次使用的是什么意思。不这样做恰恰会造成混乱。