请记住,go语言中所有的传参都是值传递,新拷贝了一个副本。
对于非引用类型的传参,是拷贝了一份新数据,修改新数据不会影响旧数据;对于引用类型(slice、map、channel…)来说,传参虽然是新拷贝了一份数据,但自身底层数据结构含有指针指向的是同一个内存地址,若修改新数据,原来的内容也会变。
什么是值传递?
传递的是原数据的副本,两个数据的地址不同
什么是引用传递?
传递的是直接是地址,这就会出现修改新数据,原数据也会改变
下面我们来看一些例子
int类型
传参后的变量地址与原始地址不同,并且不能对变量修改,若使用指针的话就可以
import "fmt"
func update(i int) {
fmt.Printf("函数内变量i的地址:%p\n", &i)
i = 55
}
func main() {
i := 5
fmt.Printf("原始变量i的地址:%p\n", &i)
update(i)
fmt.Printf("改动后的值:%v\n", i)
}
原始变量i的地址:0xc0000140e8
函数内变量i的地址:0xc000014110
改动后的值:5
再来看一下指针类型的例子
可以看出传参前后指针指向地址是同一地址,所以可以修改原数据
package main
import "fmt"
func update(i *int) {
fmt.Printf("函数内指针的地址:%p\n", &i)
fmt.Printf("函数内指针指向变量i的地址:%p\n", i)
*i = 55
}
func main() {
i := 5
n := &i
fmt.Printf("原始指针的地址:%p\n", &n)
fmt.Printf("原始指针指向变量i的地址:%p\n", n)
update(n)
fmt.Printf("改动后的值:%v\n", n)
}
原始指针的地址:0xc000006028
原始指针指向变量i的地址:0xc0000140e8
函数内指针的地址:0xc000006038
函数内指针指向变量i的地址:0xc0000140e8
改动后的值:55
slice切片类型
切片底层的结构就是一个指向数组的指针,长度len,容量cap,slice变量的地址其实就是内部存储数组元素的地址
package main
import "fmt"
func update(i []int) {
fmt.Printf("函数内切片的地址:%p\n", i)
fmt.Printf("函数内切片的第一个元素的地址:%p\n", &i[0])
i[0] = 55
}
func main() {
arr := []int{1, 2, 3}
fmt.Printf("原始切片的地址:%p\n", arr)
fmt.Printf("原始切片的第一个元素的地址:%p\n", &arr[0])
update(arr)
fmt.Printf("改动后的值:%v\n", arr)
}
原始切片的地址:0xc00000c150
原始切片的第一个元素的地址:0xc00000c150
函数内切片的地址:0xc00000c150
函数内切片的第一个元素的地址:0xc00000c150
改动后的值:[55 2 3]
map类型
使用make初始化map,内部其实传的是*hmap,修改后来的数据也会改变旧数据
package main
import "fmt"
func update(i map[int]string) {
fmt.Printf("函数内map的地址:%p\n", &i)
i[0] = "pear"
}
func main() {
m := make(map[int]string)
m[0] = "monkey"
fmt.Printf("原始map的地址:%p\n", &m)
update(m)
fmt.Printf("改动后的值:%v\n", m)
}
原始map的地址:0xc000006028
函数内map的地址:0xc000006038
改动后的值:map[0:pear]
参考runtime包中map.go
// makemap implements Go map creation for make(map[k]v, hint).(注意这一句!!!)
// If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h.
// If h.buckets != nil, bucket pointed to can be used as the first bucket.
func makemap(t *maptype, hint int, h *hmap) *hmap {
mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size)
if overflow || mem > maxAlloc {
hint = 0
}
// initialize Hmap
if h == nil {
h = new(hmap)
}
h.hash0 = fastrand()
// Find the size parameter B which will hold the requested # of elements.
// For hint < 0 overLoadFactor returns false since hint < bucketCnt.
B := uint8(0)
for overLoadFactor(hint, B) {
B++
}
h.B = B
// allocate initial hash table
// if B == 0, the buckets field is allocated lazily later (in mapassign)
// If hint is large zeroing this memory could take a while.
if h.B != 0 {
var nextOverflow *bmap
h.buckets, nextOverflow = makeBucketArray(t, h.B, nil)
if nextOverflow != nil {
h.extra = new(mapextra)
h.extra.nextOverflow = nextOverflow
}
}
return h
}
划重点!!!
- 在Go语言里,虽然只有值传递,但是我们也可以修改原内容数据,只要参数是引用类型即可
- 引用传递跟引用类型是两个概念(若在面视中遇到该问题,一定要听清面试官的问题!!)