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