Skip to content

值传递 ​

go 中, 所有的参数传递都是值传递, 即所有函数收到的都是一个副本, 只是这个副本可能是普通的值类型, 也有可能是一个指针类型而已.

简单的, 或许可以将其大体上分为“普通类型”, 共享底层数据的类型, 和组合上述两种的类型. 比如

go
var a int = 10
b := a

此处 a, b 为两个独立的变量, 修改不会互相影响.

而对于 slice / map 来说, 或许可以理解成我们持有它们只是一个 view, 所以哪怕复制了变量, 其保存的地址依旧相同, 也就是说对于一个 slice / map 复制了多份, 不是复制多份其存储的所有数据, 而是复制了这个视图, 其底层依旧指向同一段存储的数据, 所以对于这样的多个变量进行其存储的数据修改, 从宏观上会影响到所有的 Copy

接着对于复合类型就非常好解释了, 比如 struct, 其默认行为可以类似为浅拷贝.

go
type Player struct {
    HP    int
    Items []int
}

a := Player{
    HP:    100,
    Items: []int{1, 2, 3},
}

b := a

b.HP = 50
b.Items[0] = 999

fmt.Println(a.HP)       // 100
fmt.Println(a.Items[0]) // 999

也就是全部字段都会复制过去, 不过具体的复制方式看其究竟为什么类型

函数传参 ​

那么就非常的好解释, 对于“普通变量”来说, 值传递就是副本, 与原变量无关. 而对于 slice 等, 上文也提到了, 所有 view 副本指向的是同一份存储, 所以在调用的函数中进行修改, 也会对存储的数据进行修改

go
func Modify(s []int) {
    s[0] = 100
    s = s[:1]
}

func main() {
    a := []int{1, 2, 3}
    Modify(a)

    fmt.Println(a) // [100 2 3]
    fmt.Println(len(a)) // 3
}

那么如果遇到函数内修改需要影响原变量话, 传递指针即可, 不过值得注意的, 此处传递的指针依旧是原来指针的副本, 只是其存储的地址恰好指向的是原变量存储的资源, 所以对副本指针进行解引用修改的时候也会影响到原数据.

Released under the MIT License.