首页 / Go 语言入门教程 / 变量与零值

Go 语言入门教程

变量与零值

本教程共 80 篇 · 第 8 篇 · 更新于 2026-07-27 · 约 8 分钟阅读

GoGo 入门教程变量var短变量声明零值作用域Go 变量

8. 变量与零值

本节目标:掌握 Go 的两种变量声明方式,理解零值机制,搞清楚变量作用域和短变量声明的陷阱。

什么是变量

变量就是给一块内存起个名字,方便你在程序里存值和取值。

Go 是静态类型语言,每个变量都有确定的类型,类型决定了它能存什么值、占多大内存。一旦声明,类型就不能改了。

var 声明

var 是最基本的变量声明方式,格式是 var 变量名 类型

var name string   // 声明一个 string 类型的变量
var age int       // 声明一个 int 类型的变量
var pi float64    // 声明一个 float64 类型的变量

注意 Go 的类型写在变量名后面,和 C/Java 的 int age 相反。这是 Go 刻意的设计,为了从左到右阅读更自然。

声明并赋值

var name string = "Go"
var age int = 18

省略类型

如果声明时直接赋值,可以省略类型,编译器会自动推断:

var name = "Go"      // 推断为 string
var age = 18          // 推断为 int
var pi = 3.14         // 推断为 float64
var ok = true         // 推断为 bool

多变量声明

同类型多个变量:

var a, b, c int

不同类型多个变量,用括号括起来:

var (
    name   string
    age    int
    height float64
)

包级和函数级

var 声明可以出现在两个地方:

package main

var globalCount = 0  // 包级变量,整个包都能用

func main() {
    var localName = "Go"  // 函数级变量,只有 main 里能用
    fmt.Println(localName)
}

短变量声明 :=

在函数内部,Go 提供了一种更简洁的写法::=,叫做短变量声明

func main() {
    name := "Go"      // 等价于 var name = "Go"
    age := 18          // 等价于 var age = 18
    fmt.Println(name, age)
}

:= 同时做了声明和赋值,不需要写 var,也不需要写类型。这是 Go 代码里最常见的写法。

Note

:= 只能在函数内部使用,不能在包级别用。包级变量必须用 var

多变量短声明

name, age := "Go", 18
x, y := 10, 20

至少一个新变量

:= 要求左边至少有一个是新变量,否则编译报错:

a := 1
a := 2  // 报错:no new variables on left side of :=

但这种情况是可以的:

a := 1
a, b := 2, 3  // b 是新变量,a 被重新赋值

这里 a 已经存在,被赋了新值 2b 是新声明的变量。只要左边有一个新名字,:= 就合法。

Warning

这是个容易踩的坑。有时候你以为在声明新变量,其实是在重新赋值旧变量。如果旧变量在另一个作用域,可能会创建一个同名的新变量,导致逻辑错误。

零值

Go 有一个很重要的设计:变量声明后如果不赋值,会自动有一个零值

不同类型的零值不同:

类型零值
intfloat64 等数值类型0
boolfalse
string""(空字符串)
指针、切片、map、channel、函数、接口nil
数组每个元素的零值组成
func main() {
    var num int
    var flag bool
    var text string
    var ptr *int

    fmt.Println(num)   // 0
    fmt.Println(flag)  // false
    fmt.Println(text)  // (空字符串)
    fmt.Println(ptr)   // <nil>
}

零值机制的好处是:Go 里没有未初始化变量。你不会遇到 C 语言那种「变量里是随机垃圾值」的问题。

Tip

Go 的零值不是「空」或「无效」,很多类型的零值是可以直接用的。比如 bytes.Buffer 的零值就是一个可用的空缓冲区,sync.Mutex 的零值就是一个未锁的互斥锁。

变量赋值

赋值用 =

var name string
name = "Go"

age := 18
age = 20  // 重新赋值

多重赋值

Go 支持同时给多个变量赋值:

a, b := 1, 2
a, b = b, a  // 交换 a 和 b 的值
fmt.Println(a, b)  // 2 1

变量交换在 Go 里特别简洁,不需要中间变量。

不同类型的变量不能混用

a := 1     // a 是 int
a = "hello"  // 报错:cannot use "hello" as int

Go 是静态类型语言,变量类型一旦确定就不能改变。

变量作用域

作用域就是变量能被访问的代码范围。Go 用花括号 {} 来界定作用域。

函数作用域

func main() {
    x := 10  // x 只在 main 里能用

    if x > 5 {
        y := 20  // y 只在这个 if 块里能用
        fmt.Println(x, y)  // 能访问外层的 x
    }

    fmt.Println(x)  // 正确
    // fmt.Println(y)  // 报错:y 在这里不可见
}

内层可以访问外层

func main() {
    count := 0

    for i := 0; i < 3; i++ {
        count++        // 可以访问外层的 count
        fmt.Println(i, count)
    }

    fmt.Println(count)  // 3
}

外层不能访问内层

func main() {
    if true {
        secret := 42
    }
    // secret 在这里不可见
}

包级作用域

var packageVar = "I'm global"

func main() {
    fmt.Println(packageVar)  // 能访问包级变量
}

func anotherFunc() {
    fmt.Println(packageVar)  // 也能访问
}

变量必须使用

Go 有一个严格的规定:声明的变量必须被使用,否则编译报错。

func main() {
    a := 1
    b := 2  // 报错:b declared but not used
    fmt.Println(a)
}

这个规则也适用于导入的包。目的是保持代码整洁,避免无用代码堆积。

如果你确实需要声明但暂时不用,可以用空白标识符 _

func main() {
    a := 1
    b := 2
    _ = b           // 「使用」了 b,实际丢弃
    fmt.Println(a)
}

常见陷阱

短变量声明遮蔽

var count = 10

func main() {
    count := 5        // 声明了一个新的局部 count,遮蔽了外层的
    fmt.Println(count) // 5
    updateCount()
    fmt.Println(count) // 还是 5
}

func updateCount() {
    count = 20          // 修改的是包级 count
}

这里 main 里的 count := 5 创建了一个新的局部变量,和外层的 count 完全是两个东西。这种问题很难调试,要注意。

nil 不能用于短声明

x := nil  // 报错:use of untyped nil

nil 不属于任何具体类型,编译器无法推断 x 的类型。你得明确指定:

var x *int = nil
var err error = nil

学完这节你能做什么

  • var:= 两种方式声明变量
  • 理解零值机制,知道每种类型的默认值
  • 搞清楚变量作用域的层级关系
  • 避开短变量声明的常见陷阱
  • 用多重赋值交换变量

下一节讲常量和 iota