Go语言的变量
- 标准声明并初始化
var 变量名 [类型可选填] = 初始值
编译器会自动对类型进行推断
- 简化声明并初始化
变量名 := 初始值
注意:简化声明并初始化相当于先声明变量,再进行赋值,是两步操作。故而只能在函数内使用!
- 批量声明并初始化
var (
name,company = "jarvis","tencent" // string,string
age = 23 // int
)
如果声明了变量但未使用,Go 编译不会通过,会抛出错误
go变量声明书写方式的原因:
与C/C++\Java等语言反其道而行之的原因:Go语言的变量声明更符合英文语言的表达方式,不经意间就解决了复杂声明的问题,有别于C/C++还需要知道’优先级’原则、'right-left’规则等,只需要直接从左往右理解即可。避免了左右兼顾,螺旋式理解的难点。
// 指针变量声明
C Golang
int *x; <–> var x *int //x is a pointer to int
int **p; <–> var p **int //p is a pointer to pointer to int
// 数组/切片(go)变量声明
C Golang
int a[5]; <--> var a [5]int //a is an array[5] of int
int a[5][3]; <--> var a [5][3]int //a is an array[5] of array[3] of int
var s []int //s is a slice of int(C语言中无slice类型)
// 函数类型变量声明
C Golang
int (*x)(int, int) <--> var x func(int, int) int //x has the type "func (int,int) int"
// 复合类型声明
C Golang
int *x[5]; <--> var x [5]*int //x is an array[5] of pointer to int
int (*x[5])(int, int) 类似于 var x [5]func(int, int) int // x is an array[5] of "fun (int, int) int"
特性
- Go语言支持多重赋值,在进行交换变量时候十分方便,而且go中全是这么书写
func swap(a,b int) {
a,b = b,a
}
- 错误处理也使用了多重赋值,并且使用了匿名变量来充当占位符
此示例还有函数的知识,函数可以预先定义返回变量,最后直接return即可
函数也可以返回多个值,因此在接收返回值的使用需要使用多重赋值的方式,对于不想接受的返回值使用_来占位。
// 定义一个 DivideError 结构
type DivideError struct {
dividee int
divider int
}
// 实现 `error` 接口
func (de *DivideError) Error() string {
strFormat :=
`
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat, de.dividee)
}
// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
}
return varDividee / varDivider, ""
}
func main() {
// 正常情况
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 当除数为零的时候会返回错误信息
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}
Go语言全局变量
全局变量必须在函数体外声明,且必须是标准声明
var globalVal int = 0
var (
GlobalValExt int = 1
CountExt int = 2
GlobalString string = "tencent"
)
全局变量只有首字母大写才能被外部源文件通过import “packkage”的方式调用,也就说globalVal只能在自身所在的源文件被调用。而批量声明的GlobalValExt、CountExt int、GlobalString可以被外部调用。
匿名变量
_
- 不能作为右值
- 匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。
- 接收函数多个返回值的时候可以用来忽略返回值
变量的生命周期
go语言中变量一般可以分为局部变量(包括参数列表、返回值)、全局变量。go还存在逃逸分析这一概念,编译阶段编译器会自动对变量进行分析,是将变量分配在栈还是堆上。
// 定义一个全局变量gVar
var gVar *int
// 这里x会发生逃逸,被分配到堆内存中
func f() {
var x int
x = 1
gVar = &x
}
// 这里y在函数生命周期结束后就不再使用,因此y可以指向栈内存一段区域
func g() {
y := new(int)
*y = 1
}
// PS:
// 栈内存分配速度极快,且不用专门的垃圾回收算法。
// 堆内存分配略慢,内存不足以分配一段连续空间时需要进行垃圾回收
func main(){
f()
g()
}
通过命令 go run -gcflags "-m -l" main.go
来运行程序,可以打印出变量的逃逸分析情况。
-gcflags 参数是编译参数。其中 -m 表示进行内存分配分析,-l 表示避免程序内联,也就是避免进行程序优化
因此从以上代码可以看出,无论使用var还是new关键字来声明变量都不会影响编译器对变量分配方式的选择。
综上,go会自动完成内存的分配和释放,但为了能保证程序的高可用性,还是需要避免变量的逃逸,降低内存的消耗。
Go语言的常量
常量的初始化方式:
// 常量最基本的声明方式
const MAXLEN int = 10
// 常量只能通过内置函数或者unsafe.Sizeof()等函数来赋值,并且参数也要求是常量!
const MAXSIZE int = unsafe.Size(MAXLEN)
iota常量生成器
const (
a = iota
b
c
d
e
)
fmt.Println(a,b,c,d,e)
// 0 1 2 3 4
const (
i = 1 << iota
j
k
l
m
n
)
// 它可以以类似的方式来为常量做初始化。
fmt.Println(i, j, k, l, m, n)
// 1 2 4 6 8 10
自定义枚举类型
go语言目前还没有基础的枚举类型,但可以使用常量来进行定义
type Chiep int
const (
None Chiep = iota
CPU
GPU
)
// 实现chiep接口来实现从枚举类型到字符串的转换
func (c Chiep) String() string {
switch c {
case None:
return "None"
case CPU:
return "CPU"
case GPU:
return "GPU"
}
return "N/A"
}
func main() {
fmt.Printf("%s -> %d",CPU,CPU)
// CPU -> 1
}