Task3 - 变量, 常量, 枚举
typeora放到csdn上好丑= =
参考:
- Go语言圣经(中文版)
- Datawhale Task2
变量
-
格式
-
var 变量名 类型 = 表达式
-
-
类型 / 表达式 两个部分可以省略一个
- 省略表达式, 那么变量将给据类型来进行默认赋值
- 数值: 0
- 布尔: false
- 字符串: “”
- 接口/引用类型: nil
- 数组/结构体等聚合类型对应的0值为每个元素都是其对应该类型的0值
- 省略类型, 那么变量将会根据初始化的表达式来推导变量的类型
- 零值的初始化机制可以确保每个声明的变量总有一个良好的初始化定义值
- 省略表达式, 那么变量将给据类型来进行默认赋值
简短变量声明
-
格式
-
名字 := 表达式
-
-
变量的类型根据表达式来自动推导
package main
import "fmt"
func main(){
var s stringfmt.Println("s=", s)var i, j int // 隐藏表达式var n, m = 5, 5 // 隐藏类型for ; i < n; i++ {
for k := i; k < m; k++ {
// 简短变量声明fmt.Print(j)}fmt.Println()}
}
// output
c:\UserData\Go\Begin\Test>go run Task2.go
s=
00000
0000
000
00
0
- var形式的声明语句往往是用于需要显式指定变量类型的地方/因为变量稍后会被重新赋值儿初始值无关紧要
var err error
var strs []string
fmt.Println("err:", err) // err: <nil>
fmt.Println("strs:", strs) // strs: []
“:=”是一个变量声明语句,而“=”是一个变量赋值操作。也不要混淆多个变量的声明和元组的多重赋值(§2.4.1),后者是将右边各个表达式的值赋值给左边对应位置的各个变量:
i, j = j, i // 交换 i 和 j 的值
- 简短变量声明语句中必须至少要声明一个新的变量(可以混合已声明和未声明的变量, 但不能都是已声明, 否则会编译错误)
f, err := os.Open(infile)
// ...
f, err := os.Create(outfile) // compile error: no new variables
指针
- 感觉类似于c语言的指针
- &x 表示取变量x的内存地址, 产生一个指向该变量x的指针, 对应的数据类型未int*(假设x为int)
x := 1
posX := &x
fmt.Println(*posX)
*posX = 2
fmt.Println(x)
- 对调用函数内部的指针在函数结束后返回仍然安全
- 因为指针p仍然引用这个变量, 所以局部变量还是安全的
var p = f()func f() *int {
v := 1return &v
}
每次我们对一个变量取地址,或者复制指针,我们都是为原变量创建了新的别名。例如,
*p
就是变量v的别名。指针特别有价值的地方在于我们可以不用名字而访问一个变量,但是这是一把双刃剑:要找到一个变量的所有访问者并不容易,我们必须知道变量全部的别名(译注:这是Go语言的垃圾回收器所做的工作)。不仅仅是指针会创建别名,很多其他引用类型也会创建别名,例如slice、map和chan,甚至结构体、数组和接口都会创建所引用变量的别名。
new函数
另一个创建变量的方法是调用内建的new函数。表达式new(T)将创建一个T类型的匿名变量,初始化为T类型的零值,然后返回变量地址,返回的指针类型为
*T
。p := new(int) // p, *int 类型, 指向匿名的 int 变量 fmt.Println(*p) // "0" *p = 2 // 设置 int 匿名变量的值为 2 fmt.Println(*p) // "2"
Go的回收机制(变量的生命周期)
- 讲解的很清晰Orz, 直接粘贴了
基本的实现思路是,从每个包级的变量和每个当前运行函数的每一个局部变量开始,通过指针或引用的访问路径遍历,是否可以找到该变量。如果不存在这样的访问路径,那么说明该变量是不可达的,也就是说它是否存在并不会影响程序后续的计算结果。
因为一个变量的有效周期只取决于是否可达,因此一个循环迭代内部的局部变量的生命周期可能超出其局部作用域。同时,局部变量可能在函数返回之后依然存在。
编译器会自动选择在栈上还是在堆上分配局部变量的存储空间,但可能令人惊讶的是,这个选择并不是由用var还是new声明变量的方式决定的。
常量
- 常量是一个简单值的标识符,在程序运行时,不会被修改的量。数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。 常量的定义格式:(省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。)
const identifier [type] = value
const b = "abc"
多个相同类型的声明可以简写为:
const c_name1, c_name2 = value1, value2
常用于枚举:
const (Unknown = 0Female = 1Male = 2
)
0,1,2 代表未知、女、男
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过。
iota,特殊常量,可认为是可以被编译器修改的常量。在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;
package mainimport "fmt"
const (i=1<<iotaj=3<<iotakl
)func main() {
fmt.Println("i=",i)fmt.Println("j=",j)fmt.Println("k=",k)fmt.Println("l=",l)
}
结果:
i= 1
j= 6
k= 12
l= 24
枚举
枚举,将变量的值一一列举出来,变量只限于列举出来的值的范围内取值。Go语言中没有枚举这种数据类型的,但是可以使用const配合iota模式来实现
普通枚举
const (a = 0b = 1c = 2d = 3
)
自增枚举
- iota只能用于常量表达式
- 它默认开始值是0,const中每增加一行加1,同行值相同
const (a = iota //0c //1d //2
)
const (e, f = iota, iota //e=0, f=0g = iota //g=1
)
- 若中间中断iota,必须显式恢复。
const (a = iota //0b //1c = 100 //100d //100e = iota //4f //5g //6 这里恢复后仍然可以保证为自增枚举
)
(但是u1s1如果只能从1开始赋值的话 感觉这个枚举似乎作用不是很大?, 可能能改参数使得枚举赋值调整?)
- 还可以使用iota结合运算符
const (a = iota * 2bcde
)0 2 4 6 8