📅 2024年4月27日

📦 使用版本为 1.21.5

常量的定义

⭐️ 常量只可读取不可改变,相当于 Javafinal,所以说常量必须要定义一个值

⭐️ 常量的定义也有多种

1️⃣ 使用 const关键字来定义常量,和变量定义方法差不多,但是不能使用 :=

func main() {
	const a int = 10;
    const b = "a"	//不定义类型
    const a, b, c = 1, false, "str" //多重
	println(a)
}

2️⃣ 也可以多行

func main() {
	const (
		a int    = 1
		b string = "A"
	)
	println("a =", a)
	println("b =", b)
}

3️⃣ 多行在已经赋值的常量后面的常量可以不用赋值,默认值就是前一个的值

const (
	A = 1
	B // 1
	C // 1
	D // 1
	E // 1
)

func main() {
	println(A)
	println(B)
	println(C)
	println(D)
}

//输出:
1
1
1
1

⭐️ 使用 const还可以定义枚举类,使用 iota,它通常用于表示一个常量声明中的无类型整数寻序数

1️⃣ iota关键字初始值为0,然后默认在 const内递增,每个不同的数字表示不同的状态

// 定义枚举类
const (
	STUDY = iota  //初始值为0
	RUNNING		  //默认就会变成RUNNING = iota,但是此时iota为1
	EATING		  //同上iota为2
	SLEEPING
)

func main() {
	println("STUDY = ", STUDY)
	println("RUNNING", RUNNING)
	println("EATING", EATING)
	println("SLEEPING", SLEEPING)

}


//输出
PS D:\learnCode\Go\var> go run  .\test1_var.go
STUDY =  0
RUNNING 1
EATING 2
SLEEPING 3

2️⃣ 它还可以和一些运算符来搭配使用

type Allergen int


const (
    IgEggs Allergen = 1 << iota         // 1 << 0 which is 00000001
    IgChocolate                         // 1 << 1 which is 00000010
    IgNuts                              // 1 << 2 which is 00000100
    IgStrawberries                      // 1 << 3 which is 00001000
    IgShellfish                         // 1 << 4 which is 00010000
)

2️⃣ 第一个常量使用 iota值的表达式,根据序号值的变化会自动的赋值给后续的常量,直到用新的 iota重置,这个序号其实就是代码的相对行号,是相对于当前分组的起始行号

iota的值本质上就是 iota所在行相对于当前 const分组的第一行的差值

_为匿名标识符

const (
	Num  = iota<<2*3 + 1 // 1 第一行
	Num2 = iota<<2*3 + 1 // 13 第二行
	_ // 25 第三行
	Num3 //37 第四行
	Num4 = iota // 4 第五行
	_ // 5 第六行
	Num5 // 6 第七行
)

语言的变量声明

⭐️ Go有多种种定义变量的方法

⭐️ int类型在定义变量后如果未赋值,默认就是 0

⭐️ 尽管变量的标识符必须是唯一的,但你可以在某个代码块的内层代码块中使用相同名称的变量,则此时外
部的同名变量将会暂时隐藏(结束内部代码块的执行后隐藏的外部同名变量又会出现,而内部同名变量则
被释放),你任何的操作都只会影响内部代码块的局部变量。

赋值和声明

1️⃣ 使用 var和设定变量 类型,但是不赋值

func main() {
//方法一,int的默认值为0
	var a int
	fmt.Println(a)
	fmt.Printf("type of a = %T\n", a) //判断类型,这个printf和C很像 %T就表示获取当前变量的类型
}

2️⃣ 赋值

func main() {
//方法二 赋值
	var b int = 10
	fmt.Println(b)
	fmt.Printf("type of b = %T\n", b)
}

3️⃣ 还可以不设定变量类型

func main() {
//方法三,不声明变量类型
	var c = 100
	fmt.Println(c)
	fmt.Printf("type of c = %T\n", c)
		//字符串
	var d string = "hello"
	fmt.Println(d)
	fmt.Printf("cc=%s type of d = %T\n", d, d)
}

4️⃣ 甚至 var和变量类型都可以省略,直接使用 :=来代替,这个其实就是一个语法糖,叫做短变量初始化

func main() {
//方法四,省去var,它会自动推到q
	e := 1000
	fmt.Println(e)
	fmt.Printf("type of e = %T\n", e)
}

⚠️ 注意事项

  • 短变量初始化和基础变量初始化,不能为 nil

    name := nil // 无法通过编译
    
  • 短变量无法对一个已经声明的变量赋值,但是可以在赋值旧变量的时候赋值一个新变量

    // 错误示例
    var a int
    a := 1
    
    // 错误示例
    a := 1
    a := 2
    
    //正确方法
    a := 1
    a, b := 2, 2
    

⭐️ 前三种可以在全局变量中定义,而第四种不能在全局变量定义,只能在代码块内部

// A 声明全局变量前三种方法可以声明
var A int = 10

// 第四种方法,:= 只看与在函数体中运行
B := 10; //报错
func main(){
	//调用全局变量
	fmt.Println(A) //前三种

	fmt.Println(B) //第四种全局
}

⭐️ Go的多个变量声明也和其他语言有不同的地方

func main {
//声明多个变量
	//同一数据类型
	var xx, yy int = 100, 200
	fmt.Println(xx, yy)
	//多种
	var kk, ll = 100, "Abcd"
	fmt.Println(kk, ll)
	//多行变量声明
	var (
		vv int  = 100
		jj bool = false
	)
	fmt.Println(vv, jj)
}

⭐️ 在go语言中,所有在函数声明的变量都必须要被使用,但是函数外部的变量就没有这个限制

交换变量

⭐️ 在 GO中交换变量的值,可以直接使用赋值运算符交换,不需要和 C一样使用指针或者多个介值,应为go支持多重赋值和并行赋值

多重赋值指的是在一条语句中对多个变量进行赋值。

并行赋值是指在一条语句中,变量的赋值是并行发生的,也就是说,赋值操作之间没有顺序上的依赖。

func main() {
	a := 1
	b := 2
	a, b = b, a
	println(a, b)
}

⭐️ 由于在函数内部存在未使用的变量会无法通过编译,但有些变量又确实用不到,这个时候就可以使用匿名变量 _,使用 _来表示该变量可以忽略,例如

package main

import "fmt"

// 返回两个值的函数,一个整数和一个字符串
func getValues() (int, string) {
    return 42, "meaning of life"
}

func main() {
    // 只关心整数值,不关心字符串,但是函数又retrun了一个字符串
    number, _ := getValues()
  
    fmt.Println(number) // 输出: 42
}
变量比较

⭐️ go两个变量比较的类型必须一样,go语言中不存在隐式装换类型

func main() {
	var a int8
	var b int32
	println(a == b)
}

//报错
.\test1_var.go:14:15: invalid operation: a == b (mismatched types int8 and int32)

1️⃣ 但是还是可以使用强制类型转换来比较,和 Java不一样,强转的类型实在()内,它是强转的是在()外,被强转的在内

func main() {
	var a int8
	var b int32
	println(int32(a) == b) 
}

⭐️ 在没有泛型之前,早期go提供的内置 minmax函数只支持浮点数,到了1.21版本,go才终于将这两个内置函数用泛型重写。使用 min函数比较最小值

1️⃣ 实列

func main() {
	maxVal := max(-1, -2, -3) //支持负数
	minVal := min(-1,-2,-3)
	println(maxVal)
	println(minVal)
}

2️⃣ 源代码,TJava泛型表示类似

image-20240427230444274

func max[T cmp.Ordered](x T, y ...T) T

// The min built-in function returns the smallest value of a fixed number of
// arguments of [cmp.Ordered] types. There must be at least one argument.
// If T is a floating-point type and any of the arguments are NaNs,
// min will return NaN.
func min[T cmp.Ordered](x T, y ...T) T

⭐️ go中的可比较类型:

  • 布尔
  • 数字
  • 字符串
  • 指针
  • 通道 (仅支持判断是否相等)
  • 元素是可比较类型的数组(切片不可比较)
  • 字段类型都是可比较类型的结构体(仅支持判断是否相等

⭐️ 还可以通过 cmp来判断,不过仅仅支持有序类型的参数,在go中内置的有序类型只有数字和字符串

import "cmp"

func main() {
	cmp.Compare(1, 2)
	cmp.Less(1, 2)
}

1️⃣ 可以看一下源代码

// Less 函数比较两个值 x 和 y 的大小。
// 
// 参数:
// T: 实现了 Ordered 接口的类型。
// x, y: 类型为 T 的两个值,将被比较大小。
//
// 返回值:
// 返回一个布尔值,表示 x 是否小于 y。
// 如果 x 是 NaN 而 y 不是,或者 x 真正小于 y,则返回 true。
func Less[T Ordered](x, y T) bool {
	return (isNaN(x) && !isNaN(y)) || x < y
}

// Compare 函数比较两个值 x 和 y 的大小,并返回一个整数表示它们的关系。
//
// 参数:
// T: 实现了 Ordered 接口的类型。
// x, y: 类型为 T 的两个值,将被比较大小。
//
// 返回值:
// 返回一个整数,表示 x 和 y 的关系:
//   -1 如果 x 小于 y,
//    0 如果 x 等于 y,
//   +1 如果 x 大于 y。
//
// 对于浮点类型,NaN 被认为小于任何非 NaN 值,
// NaN 被认为等于任何其他 NaN 值,且 -0.0 等于 0.0。
func Compare[T Ordered](x, y T) int {
	xNaN := isNaN(x)
	yNaN := isNaN(y)
	if xNaN && yNaN { 
		return 0
	}
	if xNaN || x < y {
		return -1
	}
	if yNaN || x > y {
		return +1
	}
	return 0
}

// isNaN 函数判断给定的值 x 是否是一个 NaN(不是一个数)。
// 请注意,如果 T 不是浮点类型,此函数将总是返回 false。
//意思就是说如果是浮点数那么久会判断为true,也就输不能执行
//
// 参数:
// T: 实现了 Ordered 接口的类型。
// x: 类型为 T 的值,将被检查是否为 NaN。
//
// 返回值:
// 返回一个布尔值,如果 x 是 NaN,则返回 true。
func isNaN[T Ordered](x T) bool {
	return x != x
}

代码块

⭐️ 在函数内部,可以通过花括号建立一个代码块,代码块彼此之间的变量作用域是相互独立的。例如下面的代码

func main() {
	a := 1

	{
		a := 2
		fmt.Println(a)
	}

	{
		a := 3
		fmt.Println(a)
	}
  
	fmt.Println(a) //改变前两个代码块中的值并不会影响中国
}

//输出
2
3
1

⭐️ 但是还是会受到父块的影响

func main() {
	a := 1

	{
		a := 2
		fmt.Println(a)
	}

	{
		fmt.Println(a) //输出a
	}
	fmt.Println(a)
}

🔖 练习题

推断出这三个程序的输出

package main

var a = "G"

func main() {
	n()
	m()
	n()
}

func n() {
	print(a)
}

func m() {
	a = "O"
	print(a)
}
package main

var a = "G"

func main() {
	n()
	m()
	n()
}

func n() {
	print(a)
}

func m() {
	a := "O"
	print(a)
}

package main

var a string

func main() {
	a = "G"
	print(a)
	n()
}

func n() {
	a := "O"
	print(a)
	m()
}

func m() {
	print(a)
}