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

lhk*_*lhk 49 concurrency pass-by-reference go channels

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)
}
Run Code Online (Sandbox Code Playgroud)

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

bea*_*mit 59

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

编辑:从规范:

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

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

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

所以,以下是"参考"类型:

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

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

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

  • @tjameson:make并不意味着堆分配和切片实际上是作为结构实现的,并在传递时复制. (3认同)
  • slice的行为不像map和chan那样的“不透明指针”。如果使用参数“ a [] string”调用的函数将其更改为“ a = append(a,x)”,则调用者将看不到长度的变化。对现有索引的更改是可见的(除非在同一函数中的“ append”之前引起重新分配)。 (2认同)

new*_*cct 9

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