学习目标
理解 Go 的值语义模型(这是后续 80% Bug 的根源)
掌握变量声明、作用域与生命周期
搞清楚
for、if、switch、defer的真实行为识别并避免新手最常见的 Go 陷阱
一、Go 的核心前提:一切都是“值”
在 Go 中,变量 = 一段内存里的值。
赋值、传参、返回,默认都是拷贝。
这是 Go 与很多语言(Python / PHP / Java 引用模型)的根本差异。
二、变量声明方式(你需要全认识)
1. 显式声明
var a int = 102. 类型推断(最常用)
b := 20说明:
- := 只能在函数内部使用
- 编译期完成类型推断
3. 批量声明
var (
x int = 1
y string = "go"
)三、值拷贝:赋值的本质
示例 1:基础类型拷贝
func main() {
a := 10
b := a
b = 20
fmt.Println(a) // 10
fmt.Println(b) // 20
}过程说明:
- b := a → 把 a 的值复制一份
- 修改 b 不影响 a
结论:
赋值 = 拷贝,而不是引用
示例 2:函数参数也是拷贝
func change(x int) {
x = 100
}
func main() {
a := 10
change(a)
fmt.Println(a) // 10
}结论:
Go 的函数参数永远是值传递
四、作用域与生命周期
1. 块级作用域
if true {
x := 10
fmt.Println(x)
}
// fmt.Println(x) // 编译错误2. if 的“短变量声明”
if v := getValue(); v > 10 {
fmt.Println(v)
}- v 只存在于 if 语句块中
- 极常见、极推荐
五、控制流详解
1. if(无括号)
if a > 10 {
fmt.Println("big")
}2. for(Go 中唯一的循环)
2.1 经典 for
for i := 0; i < 3; i++ {
fmt.Println(i)
}2.2 while 风格
i := 0
for i < 3 {
i++
}2.3 无限循环
for {
break
}3. switch(Go 的 switch 非常强大)
3.1 普通 switch
switch v := 2; v {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
default:
fmt.Println("other")
}特点:
- 自动 break
- case 可写表达式
3.2 无条件 switch(代替 if-else 链)
score := 85
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
default:
fmt.Println("C")
}六、defer:延迟执行(非常重要)
1. 基本用法
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}输出结果:
hello
world2. defer 的执行顺序(栈)
func main() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
}输出结果:
3
2
1结论:
defer 是“后进先出”的栈结构
3. defer + 返回值(高频考点)
func test() int {
x := 10
defer func() {
x = 20
}()
return x
}fmt.Println(test()) // 10原因:
- return 先拷贝返回值
- defer 后执行
七、for + 闭包的经典大坑(必须理解)
错误示例(99% 新手踩过)
func main() {
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
time.Sleep(time.Second)
}可能输出:
3
3
3原因
- 闭包捕获的是 同一个 i
- goroutine 执行时,循环已结束
正确写法 1:传参
for i := 0; i < 3; i++ {
go func(v int) {
fmt.Println(v)
}(i)
}正确写法 2:重新声明变量
for i := 0; i < 3; i++ {
i := i
go func() {
fmt.Println(i)
}()
}八、今天必须建立的 5 个认知
- Go 的一切默认都是值拷贝
- 函数参数不可能“偷偷修改外部变量”
- for 是唯一循环,但足够强大
- defer 是栈,不是延时回调
- 闭包捕获的是变量,不是值
九、自检问题(进入 Day 3 前必须能回答)
- 为什么 Go 坚持值语义?
- defer 为什么是 LIFO?
- 闭包为什么会“取到错误的 i”?
- switch 为什么比 if-else 更推荐?
预告:Day 3
Day 3|指针与内存模型
你将真正搞清楚:
- 指针解决的是什么问题
- 为什么 Go 的指针是“安全的”
- new / & / nil 的本质区别
- 项目中哪些地方必须用指针