71

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 的指针。通道是否在 go 中通过引用隐式传递?

4

4 回答 4

91

从技术上讲,它们是被复制的,因为当您使用 时make,您在堆上分配一些东西,所以从技术上讲,它是幕后的指针。但是指针类型没有暴露出来,所以它们可以被认为是引用类型。

编辑:从规范:

内置函数 make 采用类型 T,它必须是切片、映射或通道类型,可选地后跟特定类型的表达式列表。它返回一个 T 类型的值(不是 *T)。内存按照初始值部分中的说明进行初始化。

必须先初始化通道,然后才能使用它。make()这样做,所以它可以用作引用类型。

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

因此,以下是“参考”类型:

  • 切片
  • 地图
  • 渠道
  • 指针
  • 功能

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

type A struct {
    b int
}
func f(a A) {
    a.b = 3
}
func main() {
    s := A{}
    f(s)
    println(s.b) // prints 0
}
于 2013-05-16T14:33:20.883 回答
13

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

于 2013-05-18T00:17:21.657 回答
3

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

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

同样“通过引用传递”意味着可以进行分配csum这将在总和之外改变它的值(而不是它的基础数据),但事实并非如此。

于 2013-05-16T14:31:15.350 回答
3

通道变量是引用,但这取决于您对“引用”的定义。语言规范从不提及引用类型。

函数中没有通道(变量)被“修改” sum。发送到通道会更改其状态。

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

编辑:上面这句话的意思是:“请注意,这对于引用语义并不是绝对必要的。”,即。“不”这个词去了MIA。对于任何最终造成的混乱,我们深表歉意。

于 2013-05-16T14:31:35.857 回答