关于Golang的指针传递

首先,我们先来看一段代码

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对于切片的操作虽然是传递的引用,但由于切片的特性,若在传递的函数中对其添加操作,其原函数中的切片是无法察觉的。因此我们尽量避免对切片参数进行添加这种情况。