疑问:切片底层不就是指针指向底层数组数据吗,为何不直接传递切片,两者有什么区别?
在源码log包中,Logger对象上绑定了formatHeader方法,它的入参对象buf,其类型是*[]byte,而非[]byte。
func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {}
func modifySlice(innerSlice []string) {
innerSlice[0] = "b"
innerSlice[1] = "b"
fmt.Println(innerSlice)
}
func main() {
outerSlice := []string{"a", "a"}
modifySlice(outerSlice)
fmt.Print(outerSlice)
}
输出结果如下:
[b b]
[b b]
将modifySlice函数的入参类型改为指向切片的指针
func modifySlice(innerSlice *[]string) {
(*innerSlice)[0] = "b"
(*innerSlice)[1] = "b"
fmt.Println(innerSlice)
}
func main() {
outerSlice := []string{"a", "a"}
modifySlice(&outerSlice)
fmt.Print(outerSlice)
}
输出结果:
&[b b]
[b b]
在上面的例子中,两种函数传参类型得到的结果都一样,似乎没发现有什么区别。通过指针传递它看起来毫无用处,而且无论如何切片都是通过引用传递的,在两种情况下切片内容都得到了修改。
这个印证了我们
一贯的认知:函数内对切片的修改,将会影响到函数外的切片。但,真的是如此吗?
type slice struct {
array unsafe.Pointer
len int
cap int
}
array是底层数组的指针,len表示长度,cap表示容量。
func modifySlice(innerSlice []string) {
innerSlice = append(innerSlice, "a")
innerSlice[0] = "b"
innerSlice[1] = "b"
fmt.Println(innerSlice)
}
func main() {
outerSlice := []string{"a", "a"}
modifySlice(outerSlice)
fmt.Print(outerSlice)
}
输出:
[b b a]
[a a]
神奇的事情发生了,函数内对切片的修改竟然没能对外部切片造成影响?为了清晰地明白发生了什么,将打印添加更多细节
func modifySlice(innerSlice []string) {
fmt.Printf("%p %v %p\n", &innerSlice, innerSlice, &innerSlice[0])
innerSlice = append(innerSlice, "a")
innerSlice[0] = "b"
innerSlice[1] = "b"
fmt.Printf("%p %v %p \n", &innerSlice, innerSlice, &innerSlice[0])
}
func main() {
outerSlice := []string{"a", "a"}
fmt.Printf("%p %v %p \n", &outerSlice, outerSlice, &outerSlice[0])
modifySlice(outerSlice)
fmt.Printf("%p %v %p \n", &outerSlice, outerSlice, &outerSlice[0])
}
输出结果如下:
0xc000004480 [a a] 0xc0000044a0
0xc000004500 [a a] 0xc0000044a0
0xc000004500 [b b a] 0xc000020080
0xc000004480 [a a] 0xc0000044a0
在Go函数中,函数的参数传递均是值传递。那么,将切片通过参数传递给函数,其实质是复制了slice结构体对象,两个slice结构体的字段值均相等。正常情况下,由于函数内slice结构体的array和函数外slice结构体的array指向的是同一底层数组,所以当对底层数组中的数据做修改时,两者均会受到影响。
但是存在这样的问题:如果指向底层数组的指针被覆盖或者修改(copy、重分配、append触发扩容),此时函数内部对数据的修改将不再影响到外部的切片,代表长度的len和容量cap也均不会被修改。
func modifySlice(innerSlice *[]string) {
//fmt.Printf("%p %v %p\n", &innerSlice, innerSlice, &innerSlice[0])
*innerSlice = append(*innerSlice, "a")
(*innerSlice)[0] = "b"
(*innerSlice)[1] = "b"
//fmt.Printf("%p %v %p \n", &innerSlice, innerSlice, &innerSlice[0])
fmt.Println(*innerSlice)
}
func main() {
outerSlice := []string{"a", "a"}
//fmt.Printf("%p %v %p \n", &outerSlice, outerSlice, &outerSlice[0])
modifySlice(&outerSlice)
//fmt.Printf("%p %v %p \n", &outerSlice, outerSlice, &outerSlice[0])
fmt.Print(outerSlice)
}
输出结果
[b b a]
[b b a]
func modifySlice(innerSlice []string) {
innerSlice = append(innerSlice, "a")
innerSlice[0] = "b"
innerSlice[1] = "b"
fmt.Println(innerSlice)
}
func main() {
outerSlice := make([]string, 0, 3)
outerSlice = append(outerSlice, "a", "a")
modifySlice(outerSlice)
fmt.Println(outerSlice)
}
输出结果:
[b b a]
[b b]