Golang的指针类型传递
程序员文章站
2022-06-22 20:28:13
...
关于Golang的指针传递
首先,我们先来看一段代码
package main
import "fmt"
func main(){
var i []int
try(i)
fmt.Println(i) //[]
}
func try(i []int){
i = append(i, 1)
}
在这里,理论上来说本应该打印[1]
,因为切片为一个指针,在try
函数对其进行了append
,其本来指向的地址也应该改变。但是,这里却为空
Golang的值传递
首先,Go这门语言对参数的传递都是值传递的,也就是说,在传递切片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
}
很明显。如果了解切片特性的人就知道,在GO的切片容量不足时,对其进行append,会新开辟一片空间,进而复制原来的新加入的,再由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
}
我们前面说了GO是指传递的,也就是说,虽然函数中的切片指向的是源切片,但是它的len和cap却是一个值,换句话说,我虽然append
了原切片,但是在主函数中的len和cap却没有改变,改变的只是原函数中的。
因此,我们要想获取函数中添加的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对于切片的操作虽然是传递的引用,但由于切片的特性,若在传递的函数中对其添加操作,其原函数中的切片是无法察觉的。因此我们尽量避免对切片参数进行添加这种情况。
上一篇: 京东3C周末大放价,盛大钜惠狂欢购