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

170 lines
4.4 KiB
Markdown
Raw Normal View History

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