在学习Go语言函数部分遇到了引用传递和值传递,与C颇为类似,浅谈一下自己对Go语言中的值传递和引用传递的理解。
一、数组—值传递
我们用Go语言中的数组作为样例,来理解值传递的过程。
代码
package main
import "fmt"
func main() {
arr1 := [4]int{1, 2, 3, 4}
fmt.Printf("arr1-p: %p\n", &arr1)
fmt.Printf("arr1[0]-p: %p\n", &arr1[0])
fmt.Println("arr1-1: ", arr1)
update(arr1)
fmt.Println("arr1-2: ", arr1)
}
func update(arr2 [4]int) {
fmt.Println("arr2-1: ", arr2)
arr2[0] = 5
fmt.Println("arr2-2: ", arr2)
fmt.Printf("arr2-p: %p\n", &arr2)
fmt.Printf("arr2[0]-p: %p\n", &arr2[0])
}
结果
arr1-p: 0xc0000141a0
arr1[0]-p: 0xc0000141a0
arr1-1: [1 2 3 4]
arr2-1: [1 2 3 4]
arr2-2: [5 2 3 4]
arr2-p: 0xc0000141e0
arr2[0]-p: 0xc0000141e0
arr1-2: [1 2 3 4]
通过运行,我们可以看到,在Go语言中对于数组传递,属于值传递,当然基本类型以及结构体在Go中也是数值传递。我们画图来理解上述结果~
从输出结果我们可以看出,arr1 和 arr2 对指向的地址与数组中的首个元素的地址相一致,说明变量 arr1 和 arr2 对应的就是数组中首元素的地址。在函数传递过程中,arr2 的数据是从 arr1 赋值或者拷贝过来的,分别存在于内存的不同空间中,所以修改 arr2 并不会对 arr1 产生影响。
补充:这里我们了解到,arr1 的地址和索引为 0 的元素的地址相同,也就是 arr1 的与索引为 0 的元素在内存的同一位置,arr1 所对应的数值为 [1 2 3 4],同理 arr2 也是如此。
对于值传递来说,在传递过程中,传递的是数据的副本,在其它函数中修改数据,并不会对原始数据产生影响。
二、切片—引用传递
我们将Go语言中与数组非常相似的切片类型作为样例,来理解引用传递的过程。
代码
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4}
fmt.Println("s1:", s1)
update2(s1)
fmt.Println("s1:", s1)
fmt.Printf("s1: %p\n", s1)
fmt.Printf("s1-p: %p\n", &s1)
}
func update2(s2 []int) {
fmt.Println("s2: ", s2)
s2[0] = 5
fmt.Println("s2: ", s2)
fmt.Printf("s2: %p\n", s2)
fmt.Printf("s2-p: %p\n", &s2)
}
结果
s1: [1 2 3 4]
s2: [1 2 3 4]
s2: [5 2 3 4]
s2: 0xc0000141a0
s2-p: 0xc0000080a8
s1: [5 2 3 4]
s1: 0xc0000141a0
s1-p: 0xc000008078
通过运行结果我们可以看到,切片在Go中属于引用传递,当然还有很多引用传递的类型,比如 map、chan等等。我们看图理解~
分析运行结果,我们能够得知,不论是 s1 还是 s2 都是指向内存中的同一地址,所以当 s2 在操作索引为 0 的数据内容时,会使得原始数据产生改变,也就是 s1 原本的数据发生变化。
补充:这里变量 s1 和 s2 的内存地址并不相同,一个是 0xc000008078,另一个是 0xc0000080a8,但是相同的是,变量 s1 和 s2 的内存地址存储的内容一致,都是切片首元素的地址,也就是索引为 0 的元素的地址。
那么对于引用传递来说,变量在内存中的存放在一定的地址上,修改变量实际上就是修改变量所在地址处的内容。