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

时间:2013-05-16 14:20:53

标签: concurrency go channels pass-by-reference

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 // 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 := <-c, <-c // receive from c

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

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

4 个答案:

答案 0 :(得分:55)

从技术上讲它们是被复制的,因为当你使用make时,你在堆上分配一些东西,所以它在技术上是一个幕后指针。但是指针类型没有公开,因此可以将它们视为引用类型。

编辑:来自规范:

  

内置函数make采用类型T,它必须是切片,地图或通道类型,可选地后跟类型特定的表达式列表。它返回类型T的值(不是* T)。内存初始化,如初始值一节中所述。

必须先初始化通道才能使用它。 Make这样做,因此它可以用作参考类型。

这基本上意味着你可以将它传递给一个函数并写入或读取它。一般的经验法则是,如果您使用makenew&,则可以将其传递给其他函数,而无需复制基础数据。

所以,以下是“参考”类型:

  • 映射
  • 信道
  • 指针
  • 功能

传递给函数时,只复制数据类型(数字,bool和结构等)。字符串是特殊的,因为它们是不可变的,但不是通过值传递的。这意味着以下内容无法按预期工作:

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

答案 1 :(得分:9)

Go中的所有内容都会传递并按值分配。某些内置类型(包括通道类型和地图类型)表现为指向某些隐藏内部结构的不透明指针。并且可以通过通道或地图上的操作来修改内部结构。它们以nil开头,类似于nil指针。

答案 2 :(得分:1)

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

请注意,切片和地图的行为方式类似,有关详细信息,请参阅http://golang.org/doc/effective_go.html

同样“通过引用传递”意味着可以在c中对sum进行赋值,这将在sum之外改变它的值(而不是它的基础数据),但情况并非如此

答案 3 :(得分:1)

通道变量是引用,但它取决于您对“引用”的定义。 Language specification从未提及引用类型。

sum函数中没有'修改'通道(变量)。发送到频道会改变其状态。

换句话说,是的,通道被实现为指向某个运行时结构的指针。请注意,这对于引用语义是非常必要的。

编辑:上面的句子意思是:“请注意, 对参考语义来说是非常必要的。”,即。 “不”这个词就是MIA。对不起最终造成的混淆。