学习目标
1. 理解 Go 的值语义模型(这是后续 80% Bug 的根源)
2. 掌握变量声明、作用域与生命周期
3. 搞清楚 for、if、switch、defer 的真实行为
4. 识别并避免新手最常见的 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
world
2. 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
Day 3|指针与内存模型
你将真正搞清楚:
- 指针解决的是什么问题
- 为什么 Go 的指针是“安全的”
- new / & / nil 的本质区别
- 项目中哪些地方必须用指针