【问题标题】:Why does assignment in Go create a copy?为什么 Go 中的作业会创建副本?
【发布时间】:2021-10-12 04:41:27
【问题描述】:

我会稍微澄清一下这个问题。我已经(几乎完全)阅读了围棋规范、常见问题解答、有效围棋,当然还有围棋之旅。

我知道 Go 是一种“按值传递”的语言,甚至设法推理这种行为并理解所有含义。

Go 中的所有作业也会创建副本。在某些情况下,它只是一个值,在某些情况下——一个指针。对于某些数据结构,它有点棘手,因为整个结构被复制并且可能包含指向另一个数据结构的隐式指针。

问题是:语言规范中有什么明确规定分配总是创建副本?

一旦你明白 Go 中没有引用,我觉得它甚至不需要提及,但是规范中关于赋值语句的部分甚至没有提到按值传递的语义。

我觉得文档中必须有一些内容详细描述了该行为,并且由于缺乏一些基本的误解,我没有意识到那里的解释。

【问题讨论】:

  • 也许混淆来自将“按值传递”和“复制”混为一谈。 Go 是按值传递,这比“复制”这个非常宽泛的术语要简单得多。
  • 例如,FAQ 文档中说“一个函数总是得到一个被传递的东西的副本,就好像有一个赋值语句将值分配给参数一样”。这涵盖了函数调用的行为,但根本没有描述赋值本身。我只是想知道这是否太明显而无法详细记录,或者我在文档中遗漏了一些东西
  • 您能否进一步解释为什么某些数据结构在复制方面“有点棘手”?
  • 我可能使用了错误的措辞。我的意思是需要时间来理解切片,复制的是切片头。这里没有什么棘手的问题,只需阅读有关切片和映射如何实现的官方博客文章即可。不过,这与我的问题无关:)
  • Why does assignment in Go create a copy?严格来说,设计决策。认为你的问题的标题更好,where in the spec does it mention that the language passes by value;

标签: go variable-assignment pass-by-value


【解决方案1】:

规范实际上在这里明确讨论了这一点:

https://golang.org/ref/spec#Calls

特别是:

在评估之后,调用的参数按值传递给函数,被调用的函数开始执行。函数的返回参数在函数返回时按值传回给调用者。

关于赋值:我所知道的大多数语言中的所有赋值都会创建值的副本(请参阅 cmets 中的 Python 例外)。 RHS 上的值的副本分配给 LHS。如果 RHS 是一个指针,则将该指针的副本分配给 LHS。

【讨论】:

  • Python 中的赋值不会复制任何内容。他们只是创建绑定
【解决方案2】:

语言规范中明确指出赋值总是会创建副本?

没有什么明确的,但你可以从Variables 中推断出这一点,它也很好地解决了函数签名的情况:

变量声明,或者对于函数参数和结果,函数声明或函数字面量的签名为命名变量保留存储空间

如果存储空间被保留,那么稍后您将一元表达式的结果分配给它 - 例如另一个变量——那么它必须是一个副本,否则你会有内存别名。这就是 Dave Cheney 在There is no pass-by-reference in Go 中所说的。

与 C++ 不同,Go 程序中定义的每个变量都占用一个唯一的内存位置。

这还有一个更重要的含义,那就是zero value。如果您在声明中未提供用于初始化变量的表达式,则为其保留的存储空间将被赋予零值作为默认值。

【讨论】:

    【解决方案3】:

    无需过多详细说明,spec 的这些摘录应该提供一些清晰性:

    变量是保存值的存储位置。

    变量的值 [...] 是分配给该变量的最新值。

    在语言级别,定义值的“复制”并不是真正必要的。正如我们通常所理解的,复制的重要含义是修改“复制到”值不会改变“复制自”值 *。这个性质可以从上面的引文中推断出来。

    • (这里需要注意的是,“值”和“数据结构”不是一回事。一个数据结构可能由多个值组成。)

    【讨论】:

      猜你喜欢
      • 2014-10-29
      • 2014-06-30
      • 1970-01-01
      • 2018-05-21
      • 2020-02-06
      • 1970-01-01
      • 2012-03-22
      • 2019-04-14
      • 1970-01-01
      相关资源
      最近更新 更多