package main

import "fmt"

func main() {
	// var 变量名字 类型 = 表达式
	var hello string = "world"
	fmt.Println(hello)

	// 其中“类型”或“= 表达式”两个部分可以省略其中的一个。
	// 如果省略的是类型信息，那么将根据初始化表达式来推导变量的类型信息。
	// 如果初始化表达式被省略，那么将用零值初始化该变量。

	// Go语言中不存在为初始化的变量

	//可以在同一句声明语句中声明不同类型的变量
	var b, f, s, i = true, 3.1415926, "four", 5 // bool float string int
	fmt.Println(b, f, s, i)

	// 可以使用简短变量声明
	// 名字 := 表达式
	operation := 1 + 10 - 6 // 5
	fmt.Println(operation)

	// 可以十分简单的交换两个变量的值
	var number1, number2 int = 2, 6          // 2, 6
	fmt.Println("before:", number1, number2) // 2, 6
	// swap。。。
	number1, number2 = number2, number1     // 交换
	fmt.Println("after:", number1, number2) // 6, 2

	// 指针
	// 一个变量对应一个保存了变量对应类型值的内存空间。
	// 声明指令变量时 *类型为该类型的指针类型
	// 例如*int代表int指针类型，*bool为bool型的指针类型
	// &变量 代表取这个变量的地址
	// 注意： 指针类型的变量本身也具有地址，可以被&取地址操作
	var point *int = &operation
	//*point 代表访问指令变量指向的变量的原始值
	fmt.Println(*point) // 5
	*point = 10 + 2     // 12
	fmt.Println(*point) // 12

	//任何类型的指针的零值都是nil。如果p指向某个有效变量，那么p != nil测试为真。
	//指针之间也是可以进行相等测试的，只有当它们指向同一个变量或全部是nil时才相等。
	var point1, point2 *int
	fmt.Println(point1 == point1, point1 == point2, point1 == nil)
	// true true true
	point1, point2 = &operation, &operation
	fmt.Println(point1 == point2) // true

	// 在Go语言中，返回函数中局部变量的地址也是安全的。
	//例如下面的代码，调用f函数时创建局部变量v，在局部变量地址被返回之后依然有效
	fun1 := func() *int {
		v := 1
		return &v
	}
	fmt.Println(fun1() == fun1()) // false

	// 另一个创建变量的方法是调用内建的new函数。
	// 表达式new(T)将创建一个T类型的匿名变量，
	// 初始化为T类型的零值，然后返回变量地址，
	// 返回的指针类型为*T
	point3 := new(int) // 创建一个*int类型的指针变量，指向匿名函数，此函数为int型
	*point3 = 34
	fmt.Println(*point3) // 34

	// new()函数和普通声明指针类型的变量没有任何区别，new()更类似于语法糖
	// 下面fun2(), fun3()两个函数有着相同的行为
	fun2 := func() *int {
		return new(int)
	}
	fun3 := func() *int {
		var temp int
		return &temp
	}
	// false 因为 两个函数申请的不同的变量
	point4, point5 := fun2(), fun3()
	fmt.Println("point4 == point5:", point4 == point5) // false
	// 每次调用new函数都是返回一个新的变量的地址，因此下面两个地址是不同的
	point4, point5 = new(int), new(int)
	fmt.Println("point4 == point5:", point4 == point5) // false

}
