--- tags: [go, golang, go基础语法, 变量] create time: 2026-06-07 15:00 --- # Go 语言变量 ## 概述 Go 提供三种变量声明方式:`var`、类型推断、`:=` 短声明。本文覆盖零值规则、多变量赋值、生命周期和常见陷阱。 ## 正文 ### 为什么 Go 的变量总有"默认值"? 在 C / C++ 中,未初始化的局部变量是**垃圾值**——读它会导致未定义行为。Go 做了一个安全选择:**所有变量都有确定的零值**。 > [!note] 📝 Go 零值表 | 类型 | 零值 | |------|------| | `int`, `int32`, `int64`, `float64` 等数值类型 | `0` | | `bool` | `false` | | `string` | `""`(空字符串) | | 指针、slice、map、chan、func、error | `nil` | ```go var i int // i == 0 var f float64 // f == 0.0 var b bool // b == false var s string // s == "" var p *int // p == nil var m map[string]int // m == nil var sl []int // sl == nil ``` > [!warning] ⚠️ nil 类型陷阱 > `nil` 不是万能值。不同类型有各自不同的 `nil` 类型:`*int` 的 nil 不等于 `[]int` 的 nil。比较时务必类型一致。 ### 三种声明方式 #### 方式一:`var` + 类型 + 初始化 ```go var a string = "Hello" // 显式类型 + 初始化 var b int // 不初始化 → 零值 0 var c bool // 不初始化 → 零值 false ``` #### 方式二:`var` + 类型推断(省略类型) ```go var d = true // 编译器推断为 bool ``` #### 方式三:`:=` 短声明(最常用) ```go e := 123 // 编译器推断为 int f := "hello" // 编译器推断为 string ``` > [!tip] 💡 首选 `:=` > 在函数体内,`:=` 是最常用的声明方式——简洁且不易出错。**但它只能用于函数体内**,不能用于包级别的变量声明。 > [!question] ❓ 思考 > 下面这段代码会编译通过吗?为什么? > ```go > var x int > x := 5 // ? > ``` ### 多变量声明与并行赋值 Go 支持一行声明多个变量: ```go // 同类型多变量 var a, b, c int // 类型推断 var d, e = 123, "hello" // 短声明(仅限函数内) f, g := 456, true ``` **并行赋值**是 Go 的一大亮点——右侧先全部求值,再一次性赋给左侧: ```go x, y := 1, 2 x, y = y, x // 交换!x 变成 2,y 变成 1 ``` 这也被广泛用于处理多返回值函数: ```go val, err := GetValue() if err != nil { // 处理错误 } ``` > [!note] 📝 并行 vs 顺序 > 在 `a[i], a[j] = a[j], a[i]` 中,Go 保证 `i` 和 `j` 的值在赋值前就被求值好,所以即使 `i == j` 也能正确工作。这是与许多语言的关键区别。 ### 空白标识符 `_` 当你不需要某个值时,用 `_` 丢弃: ```go _, b := 5, 7 // 丢弃 5,只保留 b == 7 _, err := GetValue() if err != nil { // 只需要错误,不需要返回值 } ``` > [!warning] ⚠️ `_` 不是变量 > `_` 是一个**只写占位符**,你不能读取它的值,也不能对它做任何操作。每次使用 `_` 都会创建一个新的丢弃槽。 ### 变量声明 vs 赋值 ```go var x int x := 5 // ❌ 编译错误:no new variables on left side of := x = 5 // ✅ 正确:给已声明的变量重新赋值 ``` 关键点: - `:=` **必须**在左侧引入至少一个新变量 - `=` 只是赋值,不能用来声明新变量 - 在同一代码块中,同名变量不能被 `:=` 重复声明 ### 未使用变量的陷阱 Go 编译器强制要求:**声明了就必须使用**。 ```go func main() { var a string = "abc" fmt.Println("hello") // ❌ 编译错误:a declared but not used } ``` 修复方法:使用这个变量,或者用 `_` 替代。 > [!note] 📝 全局变量例外 > 包级别声明的变量允许不使用,但函数体内的局部变量不行。这是一个常见的新手坑。 ### 生命周期 | 作用域 | 生命周期 | |--------|---------| | 全局变量(包级) | 程序启动到退出 | | 局部变量(函数内) | 函数调用开始到结束(除非发生逃逸到堆) | ```go var globalVar int // 程序整个生命周期存在 func main() { localVar := 42 // 函数返回后销毁(可能逃逸到堆) _ = localVar } ``` > [!info] ℹ️ 逃逸分析 > 如果局部变量的地址被返回或被闭包捕获,Go 的逃逸分析会将它分配到**堆**上而非栈上,这样函数返回后它依然有效。具体机制见「逃逸分析」专题。