📅 2024年4月27日
📦 使用版本为 1.21.5
常量的定义
⭐️ 常量只可读取不可改变,相当于 Java
的 final
,所以说常量必须要定义一个值
⭐️ 常量的定义也有多种
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提供的内置 min
,max
函数只支持浮点数,到了1.21版本,go才终于将这两个内置函数用泛型重写。使用 min
函数比较最小值
1️⃣ 实列
func main() {
maxVal := max(-1, -2, -3) //支持负数
minVal := min(-1,-2,-3)
println(maxVal)
println(minVal)
}
2️⃣ 源代码,T
和 Java
泛型表示类似
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)
}