package main

import "fmt"

// Golang 中的指针
func main() {

	/*
		1.关于指针
			指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的内存地址。
			Go语言中的指针操作非常简单,我们只需要记住两个符号:&(取地址)和 *(根据地址取值)。
		2.指针地址和指针类型
			每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行取地址操作。
			Go语言中的值类型(int float bool string array struct)都有对应的指针类型,如:*int、*int64、*string等。
			取变量指针的语法如下:
			ptr := &V 	// 比如V的类型为T
			则其中:
			2.1 V代表被取地址的变量类型为T
			2.2 ptr用于接收地址的变量,ptr的类型为*T,称作T的指针类型,*代表指针。
			举例如下:
				var a = 10
				var b = &a
				fmt.Printf("a:%d ptr:%p\n",a,&a)	// a:10 ptr:0xc0000100a8
				fmt.Printf("b:%v type:%T\n", b,b)	// b:0xc0000100a8	type:*int
				fmt.Printf("取b的地址:", &b)		// 0xc000006028
		3.指针取值
			在对普通变量使用&操作符取地址后会获得这个变量的指针,然后可以对指针使用*操作,也就是指针取值,代码如下
			a := 10
			b := &a	// 取变量a的地址,将地址保存到指针b中
			fmt.Printf("type of b:%T\n",b)	// type of b:*int
			c := *b // 指针取值,根据指针的值去内存取值
			fmt.Printf("value of c:%v, type of c:%T \n", c, c)	// value of c:10, type of c:int
			总结: 取地址操作符&和取值操作符*是一对互补操作符,&是取出地址,*是根据地址取出地址指向的值。
			3.1 变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
				3.1.1 对变量进行取地址&操作,可以获得这个变量的指针变量。
				3.1.2 指针变量的值是指针地址。
				3.1.3 对指针变量进行取值*操作,可以获得指针变量指向的原变量的值。
		4.指针传值示例
			func modify1(x int){
				x = 100
			}
			func modify2(x *int){
				*x = 100
			}
			func main(){
				a := 10
				modify1(a)
				fmt.Println(a)	// 10
				modify2(&a)
				fmt.Println(a)	// 100
			}

		5.new和make
			我们先来看一个例子:
			func main(){
				var userinfo map[string]string
				userinfo["username"] = "张三"
				fmt.Println(userinfo)
			}
			func main(){
				var a *int
				*a = 100
				fmt.Println(*a)
			}
			上面对于引用类型的执行代码都会引发panic异常,因为在Go 语言中对于引用类型的变量,我们在使用的时候不仅需要声明它,还要为它分配
			内存空间,否则我们的值就没有办法存储。
			而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候都已经默认分配好了内存空间。要分配内存,就引出来new和make。
			Go语言中new和make是内建的两个函数,主要用来分配内存。
			5.1 new()函数分配内存
				new是一个内置的函数,它的格式是: func new(Type) *Type
				其中:1.Type表示类型,new函数只接受一个参数,这个参数是一个类型。2.*Type表示类型指针,new函数返回一个指向该类型内存地址的指针。
				实际在开发中new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值,正因如此才不太常用它。
				a := new(int)
				b := new(bool)
				fmt.Printf("%T\n", a)	// *int
				fmt.Printf("%T\n", b)	// *bool
				fmt.Println(*a)	// 0
				fmt.Println(*b)	// false
			5.2 make()函数分配内存
				make也是用于内存分配的,区别于new,它只用于slice、map以及channel的内存创建,而且它返回的类型就是这三个类型的本身,
				而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。
				make()函数的格式是:func make(t Type, size ...IntegerType) Type
				make()函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对他们进行操作。
				示例:
					var userinfo map[string]string
					userinfo = make(map[string]string)
					userinfo["username"] = "张三"
					fmt.Println(userinfo)	// map[username:张三]
		6. new与make的区别
			1. 二者都是用来做内存分配的
			2. make只用于slice、map、channel的初始化,返回的还是这三个引用类型本身
			3. 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

	*/
	// 1.关于指针
	var a = 10
	var b = &a                         // 取变量a的地址,将地址保存到指针b中
	fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a0a8
	fmt.Printf("b:%v type:%T\n", b, b) // b:0xc00001a0a8	type:*int
	fmt.Printf("取b的地址:\n", &b)     // 0xc00000a028
	// 2.指针取值
	fmt.Printf("type of b:%T \n", b)
	c := *b                                            // 指针取值,根据指针的值去内存取值
	fmt.Printf("value of c:%v, type of c:%T \n", c, c) // value of c:10, type of c:int
	// 5.案例
	//var userinfo map[string]string
	//userinfo["username"] = "张三"
	//fmt.Println(userinfo)
	//// 这个会引发panic异常,panic: assignment to entry in nil map
	//var d *int
	//*d = 100
	//fmt.Println(*d)
	//// 这个会引发panic异常,panic: runtime error: invalid memory address or nil pointer dereference
	// 5.1 new()函数分配内存
	aa := new(int)
	bb := new(bool)
	fmt.Printf("%T\n", aa) // *int
	fmt.Printf("%T\n", bb) // *bool
	fmt.Println(*aa)       // 0
	fmt.Println(*bb)       // false
	// 指针作为引用类型需要初始化后才会拥有内存空间,才可以给它赋值。应该按照如下方式使用内置的new()函数对a进行初始化之后就可以正常对其赋值了。
	var d *int
	d = new(int)
	*d = 100 // 对指针变量进行取值*操作,可以获得指针变量指向的原变量的值,对其赋值。
	fmt.Println(*d)
	// 5.2 make()函数分配内存
	var userinfo map[string]string
	userinfo = make(map[string]string)
	userinfo["username"] = "张三"
	fmt.Println(userinfo) // map[username:张三]

	/*	上面代码的全部执行结果是:
		a:10 ptr:0xc00001a0a8
		b:0xc00001a0a8 type:*int
		取b的地址:
		%!(EXTRA **int=0xc00000a028)type of b:*int
		value of c:10, type of c:int
		*int
		*bool
		0
		false
		100
		map[username:张三]

	*/
}