这是我参与「第三届青训营 -后端场」笔记创作活动的的第4篇笔记
值传递与引用传递
go语言中有5种引用类型变量,其他的都是值类型。
slice、map、channel、interface、func()
引用类型作为参数的时候,称为浅拷贝。形参改变,实参也跟着改变。因为传递的是地址,形参和实参都指向同一块地址。
值类型作为参数的时候,称为深拷贝。形参改变,实参不变。一维传递的是值得副本,形参会开辟一块新的地址空间,与实参指向空间不同。
如果希望值类型数据在修改形参时,实参也跟着变化,可以将参数设置为指针类型。
占位符
说明
举例
输出
%p
十六进制表示,前缀 0x
Printf("%p", &people)
0x4f57f0
Slice
slice是一个包含data、cap、len的结构体reflect.SliceHeader。其中data是一个unsafe.Pointer指针类型的数组。
func main() {
//1.slice
//
var args = make([]int64, 3, 3)
args = append(args, 1)
args = append(args, 2)
args = append(args, 3)
fmt.Println(args)
//打印这个结构体的地址
fmt.Printf("切片(结构体)args的地址: %p\n", &args)
//打印底层数组指针 也就是一个元素的地址
fmt.Printf("切片(结构体)args底层数组的地址: %p\n", &(args[0]))
setValue(args)
fmt.Println(args)
}
func setValue(args []int64) {
//打印这个结构体的地址
fmt.Printf("形参切片(结构体)args的地址: %p\n", &args)
//打印底层数组指针 也就是一个元素的地址
fmt.Printf("形参切片(结构体)args底层数组的地址: %p\n", &(args[0]))
args[0] = 88889
}
-----------------------------------------------
[0 0 0 1 2 3]
切片(结构体)args的地址: 0xc000208078
切片(结构体)args底层数组的地址: 0xc000234000
形参切片(结构体)args的地址: 0xc0002080a8
形参切片(结构体)args底层数组的地址: 0xc000234000
[88889 0 0 1 2 3]
go语言中都是值传递,不存在引用传递。slice是一个结构体,因此在传递的过程中,传递的是结构体的副本,会复制结构体中的全部内容。包含底层数组的指针、长度和容量,因此这两个结构体虽然不一样,但是内部指向数组的指针是一样的。从打印结果中我们可以看到args这个结构体的地址前后不一样,但是底层数组的地址是相同的,我们改变数据,操纵的是底层数组,底层数组是一个指针,因此是可以改变数据的。
map
map/hash是一个指向runtime.hmap结构体的指针;
func makemap(t *maptype, hint int, h *hmap) *hmap {...省略代码}
func main() {
//2.map[string]int
//这是一个指针 *hmap
persons := make(map[string]int)
persons["aaa"] = 1
persons["bbb"] = 2
fmt.Println(persons)
//persons存储的地址
fmt.Printf("persons指针指向的地址: %p\n", persons)
//存储persons的地址
fmt.Printf("存储persons指针的地址: %p\n", &persons)
setValue(persons)
fmt.Println(persons)
}
func setValue(persons map[string]int) {
//persons指针指向的地址
fmt.Printf("形参persons指针指向的地址: %p\n", persons)
//存储persons该指针的内存的地址
fmt.Printf("形参存储persons指针的地址: %p\n", &persons)
persons["aaa"] = 333
}
-------------------------------------------------------
map[aaa:1 bbb:2]
persons指针指向的地址: 0xc0001b4840
存储persons指针的地址: 0xc000006db8
形参persons指针指向的地址: 0xc0001b4840
形参存储persons指针的地址: 0xc000006dc0
map[aaa:333 bbb:2]
go语言是进行值传递,那么传递的是persons这个指针内容,也就是persons指向的内容的地址。进入参数时,先为获取一块内存,存储形参persons的内容,也就是persons指针指向的那块内存的地址。
chan
chan是一个指向runtime.hchan的指针。
func reflect_makechan(t *chantype, size int) *hchan {...}
func main() {
//3.chan
// 指向hcan结构的指针 *hcan类型
p := make(chan bool)
//打印p指向的那块内存的地址,也就是p存储的地址
fmt.Printf("p存储的地址: %p\n", p)
//打印p这个*hcan类型的地址,也就是存储p的内存的地址
fmt.Printf("存储p的内存的地址: %p\n", &p)
go func(p chan bool) {
//打印p指向的那块内存的地址,也就是p存储的地址
fmt.Printf("形参p存储的地址: %p\n", p)
//打印p这个*hcan类型的地址,也就是存储p的内存的地址
fmt.Printf("形参存储p的内存的地址: %p\n", &p)
time.Sleep(time.Second * 2)
p <- true
}(p)
select {
case value := <-p:
fmt.Println(value)
}
}
----------------------------------------------------------------
p存储的地址: 0xc0000162a0
存储p的内存的地址: 0xc000006028
形参p存储的地址: 0xc0000162a0
形参存储p的内存的地址: 0xc000006030
true
解释:和map一样的原理。