Files
cs-note/hzh/GolangStar/Go语言基础/Go语言变量.md
T

170 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 的逃逸分析会将它分配到**堆**上而非栈上,这样函数返回后它依然有效。具体机制见「逃逸分析」专题。