Go 变量声明与作用域
变量是程序里用来装数据的"盒子"。Go 是一门静态类型语言,每个变量都必须有明确的类型,编译器会在编译期检查类型是否匹配,类型不对直接报错。本篇带你搞懂 Go 变量的几种声明方式、作用域规则,以及一个初学者常困惑的小符号:空白符 _。
一、四种声明方式
Go 里声明变量主要有四种写法,日常开发中最常用的是短变量声明 :=。先看一个综合示例:
package main
import "fmt"
func main() {
var a int // 先声明,零值是 0
var b int = 20 // 指定类型并赋值
var c = 30 // 省略类型,由编译器推断
d := 40 // 短变量声明(只能在函数内部用)
fmt.Println(a, b, c, d) // 0 20 30 40
}几个要点:
var a int:只声明不赋值,变量会拿到该类型的零值(下文细讲)。var b int = 20:类型和值都写明,最完整但稍显啰嗦。var c = 30:省略类型,由编译器根据右侧的值推断类型。d := 40:短变量声明,等价于var d = 40,但只能在函数内部使用,包级变量不允许这么写。
短变量声明是 Go 区别于 C/Java 的一大便利,函数内几乎到处都用它。注意 := 左侧必须至少有一个新变量,否则编译器会报"左侧没有新变量"。
二、多重赋值与变量声明组
Go 支持一次给多个变量赋值。最经典的用法是交换两个变量的值——不需要像 C 语言那样写临时变量,一行搞定:
package main
import "fmt"
// 包级变量(全局),这里不能用 :=
var x = 100
var (
name = "Go"
pi = 3.14
)
func main() {
// 多重赋值:一行声明/赋值多个变量
a, b := 1, 2
fmt.Println(a, b) // 1 2
// 经典用法:直接交换两个变量的值,不需要临时变量
a, b = b, a
fmt.Println(a, b) // 2 1
fmt.Println(x, name, pi)
}包级(全局)变量可以用 var ( ... ) 分组声明,把相关的变量集中放一起,代码更整洁。再次强调:短变量 := 在包级别是非法的,必须用 var。
三、空白符 _
Go 的函数经常返回多个值(比如一个结果加一个 error)。如果你只关心其中一个,另一个又必须接收,否则编译器会报"声明了但未使用"。这时候用空白符 _(下划线)把不需要的值丢掉即可。
package main
import "fmt"
// 返回商和余数两个结果
func divmod(a, b int) (int, int) {
return a / b, a % b
}
func main() {
// 只关心余数,用空白符 _ 把商丢掉
_, rem := divmod(17, 5)
fmt.Println("余数:", rem) // 余数: 2
// 遍历时只要值,索引用 _ 忽略
for _, v := range []int{10, 20, 30} {
fmt.Println(v)
}
}上面 _, rem := divmod(17, 5) 的意思是:我只要余数,商丢掉。_ 本质上是一个只写不读的占位符,你不能也不会去读它的值。后续处理错误时,你也常会看到 _, err := someFunc() 这种写法。
四、变量的作用域
作用域指的是变量"在哪些代码范围内能被访问"。Go 的作用域由花括号 { } 决定,分为三层:
- 包级作用域:在所有函数之外声明的变量,整个包的任何函数都能用。比如上面的
x、name。 - 函数级作用域:在函数体内声明的变量,只在该函数内有效,函数返回后消失。
- 块级作用域:在
if、for等语句的花括号内部声明的变量,出了这对花括号就不可见。
规则是:内层可以访问外层变量,外层不能访问内层变量。如果内外层同名,内层会遮蔽(shadow)外层的变量——这在 Go 里是常见踩坑点,建议尽量不要重名,免得 debug 到怀疑人生。
五、零值(Zero Value)
很多语言里变量不初始化就是脏数据(比如 C 里的随机值),Go 则保证任何变量声明后都有一个确定的初始值,称为零值。常见类型的零值如下:
- 数值类型(int、float 等):
0 - 布尔类型 bool:
false - 字符串 string:
""(空字符串) - 指针、切片、map、channel、函数、接口:
nil
package main
import "fmt"
func main() {
var i int // 0
var f float64 // 0
var s string // ""(空字符串)
var ok bool // false
var p *int // nil(指针的零值)
fmt.Printf("i=%d f=%v s=%q ok=%v p=%v\n", i, f, s, ok, p)
}这条规则让 Go 程序不会出现"未初始化导致的未定义行为",是语言层面的一种安全保障。你不必每次都记得初始化,但一定要记得检查指针或 map 是否为 nil,否则直接使用可能崩溃。
小结
记住这几条就够用了:函数内优先用 :=,包级用 var;多返回值不想全收就用 _ 丢弃;变量声明后必有零值;作用域跟着花括号走,内层同名会遮蔽外层。下一篇我们来聊常量——Go 里那个神奇的 iota。
上一篇 Go 数据类型 | 下一篇 Go 常量与 iota