通道是否隐式地通过引用传递

huangapple go评论86阅读模式
英文:

Are channels passed by reference implicitly

问题

The go tour中有一个关于通道的例子:https://tour.golang.org/concurrency/2

package main

import "fmt"

func sum(a []int, c chan int) {
    sum := 0
    for _, v := range a {
        sum += v
    }
    c <- sum // 将sum发送到c
}

func main() {
    a := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)
    x, y := <-c, <-c // 从c接收

    fmt.Println(x, y, x+y)
}

通道c在sum函数中被修改,并且在函数终止后仍然保持修改。显然,c是通过引用传递的,但没有创建c的指针。在go中,通道是否隐式地通过引用传递?

英文:

The go tour has this example for channels: https://tour.golang.org/concurrency/2

package main

import &quot;fmt&quot;

func sum(a []int, c chan int) {
    sum := 0
    for _, v := range a {
        sum += v
    }
    c &lt;- sum // send sum to c
}

func main() {
    a := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)
    x, y := &lt;-c, &lt;-c // receive from c

    fmt.Println(x, y, x+y)
}

The channel c is modified in the sum function and the changes persist after the function has terminated. Obviously c was passed by reference but no pointer to c was created. Are channels implicitly passed by reference in go ?

答案1

得分: 100

<strike>从技术上讲,它们是复制的,因为当你使用make时,你在堆上分配了一些东西,所以从技术上讲它是一个指针。但是指针类型没有暴露出来,所以它们可以被视为引用类型。</strike>

编辑:根据规范:

> 内置函数make接受一个类型T,该类型必须是切片、映射或通道类型,可选地后跟类型特定的表达式列表。它返回一个类型为T的值(而不是*T)。内存的初始化如初始值节中所述。

通道必须在使用之前进行初始化。make()可以做到这一点,因此它可以被用作引用类型。

基本上,这意味着你可以将它传递给一个函数并对其进行写入或读取。一般的经验法则是,如果你使用makenew&amp;,你可以将其传递给另一个函数而不复制底层数据。

因此,以下是“引用”类型:

  • 切片
  • 映射
  • 通道
  • 指针
  • 函数

只有数据类型(数字、布尔值和结构体等)在传递到函数时会被复制。字符串是特殊的,因为它们是不可变的,但不是按值传递的。这意味着以下代码不会按预期工作:

type A struct {
    b int
}
func f(a A) {
    a.b = 3
}
func main() {
    s := A{}
    f(s)
    println(s.b) // 输出 0
}
英文:

<strike>Technically they're copied, because when you use make, you are allocating something on the heap, so it's technically a pointer behind the scenes. But the pointer type is not exposed, so they can be thought of as a reference type.</strike>

EDIT: From the spec:

> The built-in function make takes a type T, which must be a slice, map or channel type, optionally followed by a type-specific list of expressions. It returns a value of type T (not *T). The memory is initialized as described in the section on initial values.

A channel must be initialized before it can be used. make() does this, so it can be used as a reference type.

What this basically means is that you can pass it into a function and write to or read from it. The general rule of thumb is if you use make, new or &amp;, you can pass it to another function without copying the underlying data.

So, the following are "reference" types:

  • slices
  • maps
  • channels
  • pointers
  • functions

Only data types (numbers, bools and structs, etc) are copied when passing into a function. Strings are special, because they're immutable, but not passed by value. This means that the following won't work as expected:

type A struct {
    b int
}
func f(a A) {
    a.b = 3
}
func main() {
    s := A{}
    f(s)
    println(s.b) // prints 0
}

答案2

得分: 15

在Go中,所有的东西都是通过值传递和赋值的。某些内置类型,包括通道类型和映射类型,行为类似于对某些隐藏内部结构的不透明指针。可以通过对通道或映射的操作来修改该内部结构。它们最初是nil,类似于nil指针。

英文:

Everything in Go is passed and assigned by value. Certain built-in types, including channel types and map types, behave as opaque pointers to some hidden internal structure. And it is possible to modify that internal structure by operations on the channel or map. They start out as nil, which is analogous to the nil pointer.

答案3

得分: 3

你可以说是的,但是说“通道c在sum函数中被修改”并不是真正的正确术语。通道的发送和接收并不被认为是修改。

请注意,切片和映射的行为方式类似,详细信息请参见http://golang.org/doc/effective_go.html。

此外,“按引用传递”意味着在sum中可以对c进行赋值,从而改变它的值(而不是它的底层数据),这并不是实际情况。

英文:

You could say yes, but to say the "The channel c is modified in the sum function" isn't really the correct terminology. Channel sends and receives aren't really considered modifications.

Note that slices and maps behave in a similar way, see http://golang.org/doc/effective_go.html for more details.

Also "passed by reference" implies that an assignment could be made to c in sum that would change it's value (as opposed to it's underlying data) outside of sum, which is not the case.

答案4

得分: 3

Channel variables are references, but it depends on your definition of 'reference'. Language specification never mentions reference types.

No channel (variable) is 'modified' in the sum function. Sending to a channel changes its state.

In other words, yes the channel is implemented as a pointer to some run time structure. Note that that's strictly necessary for the reference semantics.

EDIT: The above sentence was meant to read: "Note that that's not strictly necessary for the reference semantics.", ie. the word 'not' went MIA. Sorry for any eventually created confusion.

英文:

Channel variables are references, but it depends on your definition of 'reference'. Language specification never mentions reference types.

No channel (variable) is 'modified' in the sum function. Sending to a channel changes its state.

In other words, yes the channel is implemented as a pointer to some run time structure. Note that that's strictly necessary for the reference semantics.

EDIT: The above sentence was meant to read: "Note that that's not strictly necessary for the reference semantics.", ie. the word 'not' went MIA. Sorry for any eventually created confusion.

huangapple
  • 本文由 发表于 2013年5月16日 22:20:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/16589983.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定