[Go 入门] 第二章 变量

Go 入门系列参考于互联网资料与 人民邮电出版社 《Go 语言入门经典》 与 《Effective Go》,编写目的在于学习交流,如有侵权,请联系删除

变量是计算机程序不可或缺的部分, 本文内容:

变量是什么

变量就是值的引用,是实现程序逻辑的基石之一

1
2
3
func main() {
var s string = "Hello world!"
}

关于上面的解读应该为

  • 使用关键字 var 声明一个变量
  • 这个变量名为 s
  • 这个变量的类型为 string
  • 赋值运算符 = 表示将它右边的值赋给变量
  • 将字符串的字面量 “Hello world!” 赋值给变量 s

声明变量后再给它赋值

1
2
3
4
func main() {
var s string
s = "Hello world!"
}

快捷声明变量

Go 支持多种快捷变量声明方式,可在一行内声明多个类型相同的变量并给它们赋值

快捷变量声明

1
2
3
func main() {
var s, t string = "test", "func"
}

以快捷方式声明不同类型的变量

1
2
3
4
5
6
func main() {
var (
s string = "test"
i int = 4
)
}

变量声明后,就不能再次声明它,但是可以重新给它赋值

理解变量和零值

在 Go 语言中,声明变量时如果没有给它指定值,则变量将为默认值,这种默认值将被称为 零值。变量的默认值取决于其类型

1
2
3
4
5
6
7
func main() {
var i int
var f float
var b bool
var s string
fmt.Println("%v %v %v %q", i, f, b, s)
}

运行上面的代码,将会看到 0 0 false ""

使用变量时,要知道 Go 的这种决策十分的重要。在 Go 语言中要检查变量是否为已经赋值不能检查它是否为 nil ,而必须检查它的默认值,其次 Go 不允许将变量值初始化为 nil 值,因为这将会导致编译阶段错误

编写简短变量声明

1
2
3
func main() {
s := "Hello world!"
}

解读如下

  • 声明一个名为 s 的变量。这里没有指定关键字 var和类型
  • 简短变量赋值语句 := 声明使用的是简短的变量声明,这意味着不需要使用关键字变量 var,也不用指定变量的类型
  • 将字符串 “Hello world!” 赋值给 s

使用简短声明时,编译器会推断变量的类型,因此无需显式的指定变量的类型,注意,只能在函数中使用简短的变量声明

变量声明方式

下面是 Go 所有的变量声明方式:

1
2
3
4
5
var s string = "Hello world!"
var s = "Hello world!"
var t string
t = "Hello world!"
u := "Hello world!"

Go 对使用哪种方式有一定的限制 —— 不能在函数外面使用简短的变量声明,在遵守这条规则的前提下,如何声明都可以

在同一行内声明变量并给它赋值时,Go 在标准库中约定如下:在函数内使用简短变量声明,在函数外省略类型。

理解变量的作用域

作用域指的是变量在什么地方可以用,而不是变量实在什么地方声明的。 Go 语言的使用基于块的词法作用域。在 Go 语言中,块是位于一对大括号内的一系列声明和语句。但可以是空的

  • Go 语言中,一对大括号{}表示一个块
  • 对于在大括号{}内声明的变量,可以在相应块的任何地方访问
  • 大括号内的大括号定义了一个新块 —— 内部块
  • 在内部块中,可以访问外部块中声明的变量
  • 在外部块中,无法访问内部块中声明的变量
  • 代码的缩进程度反应了块作用域的层级,在每个块中,代码都被缩进
  • 在内部块中,可以引用外部块中声明的变量

使用指针

指针是另一个变量相关且必须掌握的要素,在声明变量时,将计算机内存中给它分配一个位置,以便能够储存、修改、获取变量的值。要获取变量在计算机内存中的地址,可以在变量前加上 & 字符

1
2
3
4
func main() {
s := "Hello world!"
fmt.Println(&s)
}

运行后,会得到一个内存地址 如 0xabcdef0000

将变量作为值传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
i := 1
fmt.Println(&i)
showMemAddress(i)
}

func showMemAddress(i int) {
fmt.Println(&i)
return
}

输出结果:

1
2
0xc000070090
0xc000070098

将变量传递给函数时,会分配新内存并将变量的值复制到其中。这样将会有两个变量实例,它们将位于不同的内存单元中。一般而言,这不可取,因为这将占用更多的内存,同时,由于存在变量的多个副本,很容易引入 Bug

将变量作为指针传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
i := 1
fmt.Println(&i)
showMemAddress(&i)
}

func showMemAddress(i *int) {
fmt.Println(i)
return
}

输出结果:

1
2
0xc00000a0d8
0xc00000a0d8

改动说明:

  • 将传递给 showMemAddress 的值从i改成了&i&意味着引用的是变量i的值所在的内存地址
  • 将函数 showMemAddress 的第一个参数类型从int改成了*int,加上星号意味着参数的类型为指向整数的指针,而不是整数
  • 函数中打印变量时,不需要使用 & 因为它本来就是指针

如果需要获取指针的值,那么在其前面加上 *

1
2
3
4
func showMemAddress(i *int) {
fmt.Println(*i)
return
}

声明常量

常量指的是整个程序生命周期内都不变的值,常量初始后,可以引用它,但是不能修改它

1
2
3
4
func main() {
const test string = "test"
fmt.Println(test)
}

问题列表

  • 不声明变量的类型是不是很危险?编译器在定义变量时会不会出现错误呢?

    对于未显式声明的类型的变量,Go 编译器很善于动态地推断其类型。如果编译器推断不出来(可能性很小)会提示的


All articles in this blog adopt the CC BY-SA 4.0 agreement unless otherwise stated. Please indicate the source for reprinting!