上篇文章介绍了环境搭建,并完成了学习 Go 的第一个程序 Hello World。这篇文章继续学习 Go 的基础知识,来看看变量,常量的声明与赋值。
本文所有代码基于 go1.16.6 编写。
变量
Go 编程的命名风格更习惯使用「短名称」和「驼峰式」的名称,而且大小写敏感。
开头必须是字母或者下划线,而且首字母是大写还是小写也是有特殊含义的。大写字母开头可以被包外引用,小写字母开头只能在包内使用,这个会在以后的文章中继续分享。
声明
第一种使用关键字 var
声明变量:
var name type = expression 复制代码
和 C 语言正好相反,类型是跟在变量名后面的。说实话,刚开始写 Go 代码的时候还真有些不习惯。
类型和表达式可以省略一个,但不能都省略。如果类型省略,则类型由初始化表达式决定;如果表达式省略,则初始化值为对应类型的零值。
对于数字是 0
,布尔值是 false
,字符串是 ""
,接口和引用(slice,指针,map,通道,函数)是 nil
,对于数组或结构体这样的复合类型,零值是其所有元素或成员的零值。
// 没有初始值,会赋默认零值 var v1 int var v2 string var v3 bool var v4 [10]int // 数组 var v5 []int // 数组切片 var v6 struct { e int } var v7 *int // 指针 var v8 map[string]int // map,key 为 string 类型,value 为 int 类型 var v9 func(e int) int fmt.Println(v1, v2, v3, v4, v5, v6, v7, v8, v9) // 输出 // 0 false [0 0 0 0 0 0 0 0 0 0] [] {0} <nil> map[] <nil> 复制代码
所以在 Go 中是不存在未初始化的变量的。
声明单个变量:
// 声明单个变量 var a = "initial" fmt.Println(a) // 声明布尔值变量 var d = true fmt.Println(d) 复制代码
一次声明多个变量:
// 声明多个变量 var b, c int = 1, 2 fmt.Println(b, c) 复制代码
建议以组方式一次声明多个变量:
// 以组的方式声明多个变量 var ( b1, c1 int b2, c2 = 3, 4 ) fmt.Println(b1, c1) fmt.Println(b2, c2) 复制代码
第二种方式是短变量声明:
name := expression 复制代码
使用 :=
来声明,Go 编译器会自动推断变量类型。注意 :=
和 =
的区别,前者是声明并赋值,后者是赋值。
这种初始化方式非常方便,在局部变量的声明和初始化时经常使用。
举个例子:
// 短变量声明方式 f := "short" fmt.Println(f) 复制代码
多个变量:
// 声明赋值多个变量 g, h := 5, "alwaysbeta" fmt.Println(g, h) 复制代码
有一点需要注意,多个变量声明时,最少有一个新变量,否则会报错。
var i int // i := 100 // 报错 no new variables on left side of := i, j := 100, 101 // 有新值 j,不报错 fmt.Println(i, j) 复制代码
第三种使用内置 new
函数:
p := new(T) 复制代码
初始化为类型 T
的零值,并返回其地址。
先说一下如何获取变量的地址,其实很简单,使用取地址符 &
即可。
声明一个整型的变量,然后对其取地址:
// 指针 k := 6 l := &k // l 为整型指针,指向 k fmt.Println(*l) // 输出 6 *l = 7 fmt.Println(k) // 输出 7 复制代码
使用 new
函数声明变量:
// 使用内置函数 new 声明 var p = new(int) fmt.Println(*p) // 输出整型默认值 0 *p = 8 fmt.Println(*p) // 输出 8 复制代码
再来看一个例子,下面两个函数是等价的,唯一的区别就是使用 new
少声明了一个中间变量。
func newInt() *int { return new(int) } func newInt1() *int { var p int return &p } 复制代码
赋值
使用 =
来赋值:
举例:
// 变量赋值 var m, n int m = 9 n = 10 fmt.Println(m, n) 复制代码
多重赋值:
// 变量赋值 var m, n int m = 9 n = 10 m, n = n, m fmt.Println(m, n) 复制代码
这个特性真是很爽,想想在 C 语言中是不能这么做的,要实现相同的效果,必须要借助一个中间变量才行。
如果有不需要的变量,使用空标识符 _
来忽略,在 Go 语言中,如果声明了变量而不使用,程序是会报错的。
// 空标识符 r := [5]int{1, 2, 3, 4, 5} for _, v := range r { // fmt.Println(i, v) // fmt.Println(v) // 定义 i 但不用会报错 i declared but not used fmt.Println(v) // 忽略索引 } 复制代码
作用域
变量分为全局变量和局部变量,局部变量会覆盖全局变量:
// 全局变量 var gg = "global" func main() { // 作用域 fmt.Println(gg) // 输出 global gg = "local" fmt.Println(gg) // 输出 local } 复制代码
使用流程控制语句时,需要特殊注意变量的作用域:
// 条件分支下的作用域 if f, err := os.Open("./00_hello.go"); err != nil { fmt.Println(err) } f.Close() // 报错 f.Close undefined (type string has no field or method Close) 复制代码
正确的写法:
// 正确写法 file, err := os.Open("00_hello.go") if err != nil { fmt.Println(err) } file.Close() 复制代码
常量
常量表示在程序运行过程中恒定不变的值。
声明
使用关键字 const
来声明,语法和变量类似。
一般在给常量起名的时候,会起一个有明确含义的名字。
const Pi float64 = 3.14159265358979323846 复制代码
声明单个常量:
// 无类型整型常量 const n = 500000000 // 用编译阶段即可计算出值的表达式来赋值 const d = 3e20 / n fmt.Println(d) // 无类型浮点常量 const zero = 0.0 复制代码
声明多个常量
// 无类型整型和字符串常量 const a, b, c = 3, 4, "foo" fmt.Println(a, b, c) // 多个常量 const ( size int64 = 1024 eof = -1 // 无类型整型常量 ) fmt.Println(size, eof) 复制代码
iota
常量声明还有可以使用常量生成器 iota,它不会显示写出常量的值,而是从 0 开始,逐项加 1。
// 从 0 值开始,逐项加 1 const ( a0 = iota // 0 a1 = iota // 1 a2 = iota // 2 ) fmt.Println(a0, a1, a2) // 简写,表达式相同,可以省略后面的 const ( b0 = iota // 0 b1 // 1 b2 // 2 ) fmt.Println(b0, b1, b2) const ( b = iota // 0 c float32 = iota * 10 // 10 d = iota // 2 ) fmt.Println(b, c, d) 复制代码
iota
在每个 const
开头被重置为 0。
// iota 在每个 const 开头被重置为 0 const x = iota // 0 fmt.Println(x) // 同上 const y = iota // 0 fmt.Println(y) 复制代码
还可以用来作为枚举类型,比如一周 7 天,每天用一个数字表示,那么可以这么声明:
// 枚举 const ( Sunday = iota // 0 Monday // 1 Tuesday // 2 Wednesday // 3 Thursday // 4 Friday // 5 Saturday // 6 ) fmt.Println(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday) 复制代码
总结
本文主要介绍了变量和常量的声明和赋值,变量声明主要有三种方式:
- 使用关键字
var
来声明; - 使用
:=
的短变量声明方式; - 使用内置函数
new
。
其中短变量方式在声明局部变量时经常使用,而且还要注意不要和赋值 =
弄混。
常量声明和变量类似,只需要把 var
换成 const
即可。
常量还有一种特殊的声明方式,使用 iota
。它不会显示写出常量的值,而是从 0 开始,逐项加 1。
变量的作用域分为全局变量和局部变量,局部变量可以覆盖全局变量,使用时需要注意。
文章中的脑图和源码都上传到了 GitHub,有需要的同学可自行下载。