切片的底层是三个值
- 指向底层数组首地址的指针
- Len
- Cap
所以对于以下代码
package main
import (
"fmt"
"time"
)
func f(slice []int, ch chan int) {
slice[0] = 2
ch <- 1
for i := 0; i < 10; i++ {
slice = append(slice, 4)
}
ch <- 2
slice[0] = 3
ch <- 3
}
func main() {
slice := []int{1, 2, 3}
ch := make(chan int)
go f(slice, ch)
_ = <-ch
fmt.Println(slice) // 2 2 3
_ = <-ch
fmt.Println(slice) // 2 2 3
_ = <-ch
fmt.Println(slice) // 2 2 3
time.Sleep(1)
}
看懂这个代码首先要知道go语言的赋值都是“值语义”的,也就是所有赋值本质都是拷贝了一份副本,不存在传引用的情况。
- 因为是值语义,所以形参实际是拷贝的副本,但底层指针指向的数组地址是同一个(但是指针变量本身的地址不是同一个,相当于两个指针指向了一个地址)
- 所以一开始改第一个数的时候,实参是感受的,因为底层数组是同一个
- 但是往后推一个数时,虽然底层数组也推了一个数,但函数中的len+1函数外不感受(len是拷贝的副本,函数里改了函数外不会改),所以结果还是2 2 3
- 当往后推的数到扩容时,形参的指针会指向另一个底层数组,此时再改第一个数函数外的也不感受了。
因此建议函数更改切片这种场景,直接返回改过的切片,不要依赖参数去改