【问题标题】:What is pass-reference-by-value?什么是按值传递引用?
【发布时间】:2013-05-31 12:16:49
【问题描述】:

我昨天遇到了这个问题,想知道按值传递、按引用传递和按值传递引用的关系是什么。他们的行为如何?哪种编程语言使用哪种?

另外: 我也偶尔听说过其他术语,例如 pass-by-pointer、pass-by-copy、pass-by-object……它们是如何适应的?

【问题讨论】:

  • Is Java "pass-by-reference"? 的可能重复项
  • 您不应该在一个问题中提出 5 个不同的问题。什么是按值/引用传递的问题是一百万个其他问题的重复,哪些语言使用的是一个不适合 SO 的列表问题,解释这些术语的最后一行要么是某些东西的副本,要么是需要在一个单独的问题中。这样的问题也确实需要上下文才能真正理解这些术语的含义。

标签: parameter-passing pass-by-reference pass-by-value


【解决方案1】:

让我们从基础开始。你可能已经知道这一点,或者至少认为你知道。令人惊讶的是,许多人实际上相当多地滥用了这些术语(这当然无助于混淆)。

什么是传值和传引用?

我现在不打算告诉你答案,而是用例子来解释它。稍后你会明白为什么。

假设您正在尝试学习一门编程语言,而一位朋友告诉您他知道一本关于该主题的好书。你有两个选择:

  1. 向他借书或
  2. 购买您自己的副本。

案例 1:如果您借他的书并在页边空白处做笔记(并添加一些书签、参考资料、图纸……),您的朋友以后也可以从这些笔记和书签中受益。分享是关怀。世界真好!

案例 2:但是,另一方面,如果您的朋友有点爱整洁,或者非常珍惜他的书,他可能不会欣赏您的笔记,更不用说偶尔的咖啡渍或您如何折叠角落以添加书签.你真的应该得到你自己的书,每个人都可以愉快地以他喜欢的方式对待他的书。世界真好!

这两种情况——你猜对了——就像传递引用和传递值一样。 区别在于函数的调用者是否可以看到被调用者所做的更改。

让我们看一个代码示例。 假设你有以下代码 sn -p:

function largePrint(text):
  text = makeUpperCase(text)
  print(text)

myText = "Hello"
largePrint(myText)
print(myText)

然后你用传值语言运行它,输出将是

HELLO
Hello

在传递引​​用的语言中,输出将是

HELLO
HELLO

如果你通过引用传递变量“myText”被函数改变。如果您只是传递变量的值,则函数无法更改它。

如果你需要另一个例子,this 一个使用网站的人得到了很多支持。

好的,现在我们已经掌握了基础知识,让我们继续...

什么是按值传递引用?

让我们再次使用我们的书籍示例。假设您决定向您的朋友借这本书,尽管您知道他是多么喜欢他的书。让我们也假设你当时——作为一个完全的笨蛋——把一加仑咖啡洒在上面。天啊... 不用担心,你的大脑会启动并意识到你可以给你的朋友买一本新书。它的状况如此之好,他甚至都不会注意到!

这个故事与按值传递引用有什么关系?

好吧,另一方面,想象一下你的朋友非常喜欢他的书,他不想借给你。然而,他确实同意把这本书带来给你看。问题是,你仍然可以将一加仑咖啡洒在上面,但现在你不能再给他买一本新书了。他会注意到的!

虽然这听起来很可怕(并且可能让你像喝了那加仑咖啡一样出汗),但它实际上很常见,并且是 share book 调用函数的绝佳方式。它有时被称为按值传递引用。

代码示例的表现如何?

在按值传递引用的函数中,我们的 sn-p 的输出是

HELLO
Hello

就像在按值传递的情况下一样。但是如果我们稍微修改一下代码:

function largePrint(text):
  text.toUpperCase()
  print(text)

myText = "Hello"
largePrint(myText)
print(myText)

输出变成:

HELLO
HELLO

就像在传递引用的情况下一样。原因与书籍示例中的相同:我们可以通过调用 text.toUpperCase() (或将咖啡洒在上面)来更改变量(或书籍)。但是我们不能再改变变量引用的对象(text = makeUppercase(text) 没有效果);换句话说,我们不能使用不同的书。

总结一下,我们得到:

按值传递:

你得到了你自己的书,你自己的变量副本,以及你用它做的任何事情,原始所有者永远不会知道。

参考传递:

您使用与您的朋友相同的书或与调用者相同的变量。如果您更改变量,它会在您的函数之外更改。

按值传递引用

你和你的朋友使用的是同一本书,但他并没有把它从他的视线中移开。你可以换书,但不能换书。在变量方面,这意味着您不能更改 what 变量引用,但您可以更改 which 变量引用。

现在,当我向您展示以下 Python 代码时:

def appendSeven(list):
    list.append(7)

def replaceWithNewList(list):
    list = ["a", "b", "c"]

firstList = [1, 2, 3, 4, 5, 6]
print(firstList)
appendSeven(firstList)
print(firstList)

secondList = ["x", "y", "z"]
print(secondList)
replaceWithNewList(secondList)
print(secondList)

并告诉你输出是这样的

[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
['x', 'y', 'z']
['x', 'y', 'z']

你说什么?

确实,你是对的! Python 使用按值传递引用! (Java、Ruby 和 Scheme 也是如此……)

结论:

这非常困难吗?不。 那为什么那么多人会被它迷惑呢? (我不会链接到有关“如何在 Python 中通过引用传递变量”或“Java 是否通过引用传递?”等所有其他问题......)

嗯,我看到了几个原因:

  1. 说按引用传递值真的很长(无论如何它的行为与按引用传递几乎相同,所以请放下我的背,停止分裂!)
  2. 有一些令人讨厌的特殊情况使问题复杂化
    • 类似于原始类型(在按引用传递值的语言中通常是按值传递)
    • 不可变类型(无法更改,因此通过引用传递它们不会让您到达任何地方)
  3. 有些人只是喜欢使用不同的名称:
    • pass-by-copy 用于 pass-by-value,因为如果您注意,您需要自己的书,因此必须制作副本。 (但这是一个实现细节,其他技巧(如写时复制)可能仅用于制作副本。)
    • pass-by-reference 用于 pass-reference-by-value 因为毕竟您确实获得了引用,这意味着您可以更改调用者拥有的对象(即使你不能交换它)
    • pass-by-reference 用于 pass-reference-by-value,因为几乎没有语言实际上具有 pass-by-reference(值得注意的例外(我知道):C++、C#但是你必须使用特殊的语法)
    • pass-by-sharing 用于 pass-reference-by-value(还记得我们书中的例子吗?这个名字实际上更好,每个人都应该使用它!)
    • pass-by-object 用于 pass-reference-by-value,因为据称 Barbara Liskov 首先命名它并使用它,所以这是它的出生名)
    • pass-by-pointer 用于 pass-by-reference,因为 C 没有 pass-by-reference,这就是它允许您完成相同事情的方式。

如果您认为我应该添加任何内容,请告诉我。如果我犯了错误,请告诉我!

对于那些还没有足够的人:http://en.wikipedia.org/wiki/Evaluation_strategy

【讨论】:

  • 对于一个实际上并不那么棘手的概念,这似乎是一个极其复杂、冗长的解释。几个代码示例可能就足够了......
  • @OliCharlesworth 可能仅供参考,但我认为示例非常适合第一次学习这个概念。如果您愿意,请添加答案。
  • 哦,我的意思是,您可能可以摆脱所有冗长的文本类比,而只使用比较伪代码示例。这些类比只是混淆了这个概念。
【解决方案2】:

这是一个虚构的术语,指的是实际上是按值传递的东西,但人们总是会感到困惑,因为语言中唯一的值是引用(指向事物的指针)。许多现代语言都以这种方式工作,包括 Java(用于非原始语言)、JavaScript、Python、Ruby、Scheme、Smalltalk 等。

初学者总是在 StackOverflow 上询问为什么它不是按引用传递的问题,因为他们不了解按引用传递的真正含义。所以为了让他们满意,有些人编造了一个他们也不理解的新术语,但让人觉得他们学到了一些东西,听起来有点像 pass-by-value 和 pass-by-reference 。

不同语言社区对术语的使用差异很大。例如,Java 社区几乎总是将这种语义描述为按值传递,而 Python 和 Ruby 社区很少将其描述为按值传递,即使它们在语义上是相同的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-05
    • 1970-01-01
    • 2015-03-24
    • 2019-04-04
    • 2014-04-12
    • 2016-11-28
    • 2017-11-14
    相关资源
    最近更新 更多