首先,我们先来看一段代码
package main
import "fmt"
func main(){
var i []int
try(i)
fmt.Println(i) //[]
}
func try(i []int){
i = append(i, 1)
}
[1]tryappend
Golang的值传递
i
package main
import "fmt"
func main(){
var i []int=[]int{0}
try(i)
fmt.Printf("%p\n",&i[0]) // 0xc00000a0b0
fmt.Printf("%p\n",&i) // 0xc0000044a0
}
func try(i []int){
fmt.Printf("%p\n",&i[0]) // 0xc00000a0b0
fmt.Printf("%p\n",&i) // 0xc0000044c0
}
这里你就可能明白了,对切片的传递,其实是传递的引用,也就相当于新建一个指针,进而指向该切片。
但是,既然都指向了该切片了,理论上append该切片的话,也应该能打印出来,而一开头代码却无法打印,我们接着往下看
切片的特性
老规矩,先看一段代码
package main
import "fmt"
func main() {
i:=[]int{1}
try(i)
}
func try(i []int) {
fmt.Printf("%p\n", &i[0]) // 0xc00000a0b0
i = append(i, 1)
fmt.Printf("%p\n", &i[0]) // 0xc00000a0e0
}
append
这也就是为什么一开头的代码无法打印出新加入值的原因。
那么,我们给初始化的切片足够容量,是不是就可以打印了?
func main() {
i:=make([]int,1,2)
try(i)
fmt.Println(i) // [0]
fmt.Printf("%p\n",&i[0]) // 0xc00000a0b0
}
func try(i []int) {
i = append(i, 1)
fmt.Println(i) // [0,1]
fmt.Printf("%p\n",&i[0]) // 0xc00000a0b0
}
很明显,虽然两个切片头的地址都一样,但还是打印不了,
对于这一点,我们得知道切片在GO代码中的是如何定义的
type slice struct {
array unsafe.Pointer
len int
cap int
}
append
因此,我们要想获取函数中添加的1,也不是不可以
func main() {
i:=make([]int,1,2)
try(i)
ptr:=uintptr(unsafe.Pointer(&i[0]))
fmt.Println(*(*int)(unsafe.Pointer(ptr + 8))) // 1
}
func try(i []int) {
i = append(i, 1)
}
这里,我们将切片的第一个元素的指针往后移8字节,就找到新添加的了。
总结Go对于切片的操作虽然是传递的引用,但由于切片的特性,若在传递的函数中对其添加操作,其原函数中的切片是无法察觉的。因此我们尽量避免对切片参数进行添加这种情况。