slice&map&chan|青训营笔记

107 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第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一样的原理。