一、变量
1.1 声明变量
x int //声明变量
p *int //声明指针
a [3]int //声明数组
func f(a int, b int) int //声明函数,函数名为f
<说明> Go语言的变量声明是以var关键字开头,后置变量类型,行尾无须分号。
2、批量格式
var (
a int
b string
c []float32
d func() bool
e struct {
x int
}
)
批量格式使用var和小括号(),可以将一组变量定义在一起。
指针
var a []int //声明int型切片
x = a[1] //将切片元素赋值给变量x
//类似的,Go语言的指针沿用了C的星号(*)的记法,声明的时候星号是在变量名的右边,但在表达式语法中却又把星号放在变量的左边:
var p *int //声明int型指针p
x = *p //将指针p所指向的内存单元的值赋值给变量x
可以看到,Go语言的指针的声明与C语言是不一样的,但是对指针的运算使用的语法是相同的。
1.2 初始化变量
Go语言在声明变量时,会自动对变量对应的内存区域进行初始化操作。每个变量都被初始化其类型的默认值。例如:
- 整型和浮点型变量的默认值是0。
- 字符串变量的默认值为空字符串。
- 布尔型变量的默认值是false。
- 切片、函数、指针变量、接口变量的默认值是nil。
var hp int = 100
2、编译器推导类型的格式
在上面标准的基础上,将类型int省略,编译器会根据等号右边的表达式推导出等号右边的hp变量的类型。因此,上面的语句简写成下面的语句:
var hp = 100
等号右边的部分在编译原理中被称作“右值”。
下面的一个例子:
var attack 40
var defence 20
var damageRate float32 = 0.17
var damage = float32(attack-defence) * damageRate
Go语言和C语言一样,编译器会尽量提供精确度,以避免计算中精度损失。默认情况下,如果不指定damageRate变量的类型,Go语言编译器会将damageRate类型推导为float64。本例中,指定了其类型为float32,所以不需要float64的精度。
float(attack-defence)将两个整型变量相减后的结果强制转换为float32类型,然后再与float32类型的damageRate相乘,因此damage类型也是float32类型。
<建议> 在实际的开发过程中,不太建议使用这种简写的变量初始化方式,最好还是带上类型,即使用标准格式来初始化变量。
3、短变量声明并初始化
var的变量声明还有一种更为精简的写法,例如:
hp := 100
这是Go语言的推导声明写法,编译器会自动根据右值类型推断出左值的对应类型。
<注意> 由于使用了":=",而不是赋值符号"=",因此推导声明的左值变量必须是一个没有定义过的变量。若已经定义过,将会发生编译报错。例如:
var hp int
hp := 100
编译时会报错:no new variables on left side of :=
提示,在:=左边没有新变量出现,意思就是“:=” 的左边变量已经被声明过了。短变量的声明方式在开发中的例子较多,例如:
conn, err := net.Dial("tcp", "127.0.0.1:8080")
如果是标准声明格式,将会变成:
var conn net.Conn
var err error
conn, err := net.Dial("tcp", "127.0.0.1:8080")
因此,短变量声明并初始化的格式在开发中使用比较普遍。因为它可以简化代码的书写。
《注意》在多个短变量声明和赋值时,至少要有一个新声明的变量出现在左值中,即便其他变量名是重复声明的,编译器也不会报错。
例如:
conn, err := net.Dial("tcp", "127.0.0.1:8080")
conn2, err := net.Dial("tcp", "127.0.0.1:8080")
上面的代码片段中,编译器不会报err重复定义的错误,因为 conn2 是新声明的变量。
1.3 多个变量同时赋值(多重赋值)
使用Go语言的“多重赋值”特性,可以轻松完成两个变量的交换。
var a int = 100
var b int = 200
b, a = a, b
- 多重赋值时,变量的左值和右值按从左到右的顺序赋值。即先是=号右边的a先赋值给左边的b,然后是=号右边的b赋值给=号左边的a。
- 多重赋值在Go语言的错误处理和函数返回值中会大量地使用。
例如,使用Go语言进行排序时就需要使用多重交换。
type IntSlice []int //将[]int声明为IntSlice类型,即类型别名
func (p IntSlice) Len() int { return len(p) } //切片长度函数
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] } //根据索引比较元素的大小并返回结果
func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } //根据索引交换两个元素的值
1.4 匿名变量
在使用多重赋值时,如果不需要在左值中接收变量值,可以使用匿名变量。Go语言中匿名遍历是一个下划线"_"。使用匿名变量时,只需要在变量声明的地方使用下划线替换即可。
例如:
func GetData() (int, int) {
return 100, 200
}
a, _ := GetData()
_, b := GetData()
匿名变量的特点:匿名变量不占用内存空间,不会进行内存分配。匿名变量与匿名变量之间也不会因为多次声明而无法使用。可以将匿名变量理解为一种占位符。
二、常量
常量是恒定不变的量,例如圆周率。可以在编译器对常量表达式进行计算求值,并在运行期使用该计算结果,但是计算结果无法被修改。
常量的声明使用关键字const。例如:
const PI = 3.1415926
const e = 2.718281
多个常量也可一起声明,使用const关键字+小括号。例如:
const (
PI = 3.1415926
e = 2.718281
)
因为常量的值是在编译阶段就确定了,所以可以用于数组声明。例如:
const sise = 4
var arr [size]int
2.1 枚举常量 —— 一组常量值
Go语言目前还没有枚举类型(目前最新版是Go1.15),但是可以使用常量配合itoa模拟枚举常量。示例:
type Weapon int //声明一个类型别名
const (
Arrow Weapon = itoa //开始生成枚举值,默认值为0
Shuriken
SniperRifle
Rifle
Blower
)
//输出所有枚举值
fmt.Println(Arrow, Shuriken, SniperRifle, Rifle, Blower)
//使用枚举类型并赋值
var weapon Weapon = Blower
fmt.Println(weapon)
代码输出结果如下:
0 1 2 3 4
4
《代码说明》
- 将int类型定义为Weapon类型,这只是为int类型设置了一个类型别名(Type Alias)。就像枚举类型其实本质是整型一样。当然,某些情况下,如果需要int32和int64的枚举,也是可以的。
- 将Arrow常量的类型标识为Weapon类型后,const下方的常量可以是默认类型的,默认时,默认使用前面指定的类型作为常量类型。因此Arrow后面的常量默认也都是Weapon类型了。使用itoa可以进行常量值的自动生成。itoa的起始值是0,一般情况下也是建议枚举值从0开始。一个const声明内的每一行常量声明,将会自动套用前面的itoa格式,并自动增加,默认增加1。
- 当然,itoa不仅仅只生成每次增加1的枚举常量。还可以利用itoa来做一些强大的枚举常量值生成器。例如,下面的代码可以方便生成标志位常量:
const (
FlagNone = 1 << itoa
FlagRed
FlagGreen
FlagBlue
)
fmt.Printf("%d %d %d\n", FlagRed, FlagGreen, FlagBlue)
fmt.Printf("%b %b %b", FlagRed, FlagGreen, FlagBlue) //以二进制格式输出
/*
程序描述:将枚举值转换成字符串。
说明: Go语言没有枚举类型,可以使用常量配合iota模拟枚举。
*/
package main
import "fmt"
//将ChipType定义为int类型
type ChipType int
const (
None ChipType = iota //iota从0开始计数
CPU
GPU
)
//定义ChipType类型的方法String(),返回字符串
func (c ChipType) String() string {
switch c {
case None:
return "None"
case CPU:
return "CPU"
case GPU:
return "GPU"
}
return "N/A"
}
func main() {
//输出CPU的值并以整数格式显示
fmt.Printf("%s %d\n", CPU, CPU)
}
输出结果:CPU 1
《代码说明》这里我们实现了ChipType类型的String()方法,当这个类型需要显示为字符串时,Go语言会自动寻找String()方法并进行调用。输出格式%s对应的是字符串类型,%d对应的是整型。