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
}

几个要点:

短变量声明是 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 的作用域由花括号 { } 决定,分为三层:

规则是:内层可以访问外层变量,外层不能访问内层变量。如果内外层同名,内层会遮蔽(shadow)外层的变量——这在 Go 里是常见踩坑点,建议尽量不要重名,免得 debug 到怀疑人生。

五、零值(Zero Value)

很多语言里变量不初始化就是脏数据(比如 C 里的随机值),Go 则保证任何变量声明后都有一个确定的初始值,称为零值。常见类型的零值如下:

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

✈️💬