前言
Go中所有的参数传递都是值传递,是对数据进行一个拷贝。具体分为引用类型和非引用类型
-
非引用类型(值类型):int,string,float,bool,数组和struct;
特点:值类型变量声明后,直接存的就是对应的数据。
-
引用类型:指针,slice,map,channel,接口,函数等。
特点:变量存放的是一个内存地址值,这个地址值指向的空间存的才是最终的值。内存通常在堆中分配,当没有任务变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,通过GC回收。
这里我所说的值传递,意思就是引用类型在传递的时候,其实是将值实际地址,也就是当前变量所存的地址拷贝一份进行传递。
值传递
代码:
package main
import "fmt"
func main() {
var value = 3
//因为int为值类型 所以需要取地址符
fmt.Printf("modify before addr: %p value: %d\n", &value, value)
modify(value)
//再次打印
fmt.Printf("modify after main addr: %p value: %d\n", &value, value)
}
func modify(value int) {
value = 0
fmt.Printf("modify addr: %p value: %d\n", &value, value)
}
输出:
modify before addr: 0xc000018080 value: 3
modify addr: 0xc000018088 value: 0
modify after main addr: 0xc000018080 value: 3
可以发现地址两个地址不是同一个地址,并且也不会对实参进行修改
扫描二维码关注公众号,回复:
16505035 查看本文章
引用传递
此时我们使用slice进行举例说明
代码:
package main
import "fmt"
func main() {
value := []int{
1, 2, 3}
fmt.Printf("modify begin addr: %p value: %d\n", value, value)
fmt.Printf("modify begin addr: %p value: %d\n", &value, value)
modify1(value)
fmt.Printf("modify after addr: %p value: %d\n", value, value)
fmt.Printf("modify after addr: %p value: %d\n", &value, value)
}
func modify1(value []int) {
value[0] = 0
fmt.Printf("modify addr: %p value: %d\n", value, value)
fmt.Printf("modify addr: %p value: %d\n", &value, value)
}
结果:
modify begin addr: 0xc0000b4000 value: [1 2 3]
modify begin addr: 0xc0000a4018 value: [1 2 3]
modify addr: 0xc0000b4000 value: [0 2 3]
modify addr: 0xc0000a4078 value: [0 2 3]
modify after addr: 0xc0000b4000 value: [0 2 3]
modify after addr: 0xc0000a4018 value: [0 2 3]
可以发现
这个地址不论是实参还是函数内部地址都是相同的,而另外一个地址是不同的。
这是因为切片是一个引用类型,然后这个变量里面保存的是实际数据的地址,而在进行参数传递的时候,我们传递的是保存的地址,而不是保存这个地址的地址。也就是说我们将这个地址值传递过去了,然后函数内部开辟了一个内存将这个地址存起来了,然后我们就可以看见无论是在外面还是里面,这个传递的地址值是不变的。而保存这个地址的地址是改变的。这也是为什么是golang是值传递的原因。
所以在函数中对数据进行修改,其实是对保存地址中的数据进行修改,所以会影响到函数外。