Add Chapter 2: Go Basics - Variables, Types, and Flow Control (Deep Dive)
This commit is contained in:
896
chapters/chapter-2-basics.md
Normal file
896
chapters/chapter-2-basics.md
Normal file
@@ -0,0 +1,896 @@
|
||||
# 第二章:Go 基础语法 —— 变量、类型与流程控制
|
||||
|
||||
> **本章目标**:深入理解 Go 的变量声明机制、核心数据类型、运算符体系及流程控制结构,掌握 Go 独特的"零值"哲学和"类型推断"特性。
|
||||
|
||||
## 2.1 Go 的变量声明机制
|
||||
|
||||
Go 语言的变量声明方式灵活多样,理解其背后的机制是编写高效代码的基础。
|
||||
|
||||
### 2.1.1 四种声明方式
|
||||
|
||||
#### 1. `var` 声明(显式类型)
|
||||
|
||||
```go
|
||||
var name string = "Alice"
|
||||
var age int = 25
|
||||
var isActive bool = true
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `var` 是 Go 中**最正式**的声明方式
|
||||
- 可以省略初始化,此时变量会被赋予**零值**(见 2.2 节)
|
||||
- 支持在函数内外声明(包级变量和局部变量)
|
||||
|
||||
#### 2. `var` 声明(类型推断)
|
||||
|
||||
```go
|
||||
var name = "Bob" // 编译器推断为 string
|
||||
var count = 100 // 编译器推断为 int
|
||||
var ratio = 3.14 // 编译器推断为 float64
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- 当提供初始值时,Go 编译器会自动推断类型
|
||||
- 这是**推荐**的声明方式,代码更简洁
|
||||
- 类型一旦推断,不可更改(Go 是静态类型语言)
|
||||
|
||||
#### 3. 短变量声明 `:=`(最常用)
|
||||
|
||||
```go
|
||||
name := "Charlie"
|
||||
age := 30
|
||||
isValid := true
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `:=` 是 `var` + 类型推断的**语法糖**
|
||||
- **只能在函数内部使用**(不能在包级作用域使用)
|
||||
- 如果变量已存在,会**重新赋值**而非声明新变量
|
||||
- 可以混合新旧变量:`a, b := 1, 2`(即使 `a` 已存在)
|
||||
|
||||
```go
|
||||
// 错误示例:包级作用域不能使用 :=
|
||||
package main
|
||||
|
||||
// x := 10 // 编译错误!
|
||||
|
||||
func main() {
|
||||
y := 20 // 正确
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 多变量声明
|
||||
|
||||
```go
|
||||
// 显式类型
|
||||
var (
|
||||
name string = "David"
|
||||
age int = 28
|
||||
)
|
||||
|
||||
// 类型推断
|
||||
var (
|
||||
city = "Beijing"
|
||||
score = 95.5
|
||||
)
|
||||
|
||||
// 短声明
|
||||
x, y, z := 1, 2, 3
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- 使用 `var ()` 块可以批量声明变量,适合包级变量
|
||||
- 短声明 `:=` 支持多个变量同时声明
|
||||
- **注意**:`:=` 左侧至少有一个变量是新的,否则是赋值操作
|
||||
|
||||
```go
|
||||
a, b := 1, 2 // 声明两个新变量
|
||||
a, c := 3, 4 // a 已存在,c 是新变量(合法)
|
||||
a, b := 5, 6 // a 和 b 都已存在,编译错误!
|
||||
```
|
||||
|
||||
### 2.1.2 变量作用域
|
||||
|
||||
Go 的作用域规则非常清晰:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// 包级变量(全局作用域)
|
||||
var globalVar = "I am global"
|
||||
|
||||
func main() {
|
||||
// 函数级变量
|
||||
localVar := "I am local"
|
||||
|
||||
if true {
|
||||
// 块级变量(if 块内)
|
||||
blockVar := "I am in if block"
|
||||
fmt.Println(blockVar)
|
||||
}
|
||||
|
||||
// fmt.Println(blockVar) // 编译错误!blockVar 在 if 块外不可见
|
||||
|
||||
fmt.Println(localVar)
|
||||
fmt.Println(globalVar)
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- **包级作用域**:在 `package` 声明后,任何函数外声明的变量
|
||||
- **函数作用域**:在函数内部声明的变量
|
||||
- **块级作用域**:在 `{}` 代码块内声明的变量(if、for、switch 等)
|
||||
- 内层作用域可以**遮蔽**外层同名变量
|
||||
|
||||
```go
|
||||
func shadowExample() {
|
||||
x := 10
|
||||
if true {
|
||||
x := 20 // 创建了新变量 x,遮蔽了外层的 x
|
||||
fmt.Println(x) // 输出 20
|
||||
}
|
||||
fmt.Println(x) // 输出 10(外层 x 未受影响)
|
||||
}
|
||||
```
|
||||
|
||||
## 2.2 Go 的"零值"哲学
|
||||
|
||||
Go 语言有一个非常优雅的设计:**未初始化的变量会自动获得零值**。
|
||||
|
||||
### 2.2.1 零值表
|
||||
|
||||
| 类型 | 零值 | 说明 |
|
||||
|------|------|------|
|
||||
| `int`, `int8`, `int16`, `int32`, `int64` | `0` | 整数类型 |
|
||||
| `uint`, `uint8`, `uint16`, `uint32`, `uint64` | `0` | 无符号整数 |
|
||||
| `float32`, `float64` | `0.0` | 浮点数 |
|
||||
| `complex64`, `complex128` | `0 + 0i` | 复数 |
|
||||
| `bool` | `false` | 布尔值 |
|
||||
| `string` | `""` | 空字符串 |
|
||||
| 指针、函数、接口、切片、映射、通道 | `nil` | 引用类型 |
|
||||
| 数组 | 数组元素全为零值 | 值类型 |
|
||||
|
||||
### 2.2.2 零值的实际意义
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var intVar int
|
||||
var floatVar float64
|
||||
var boolVar bool
|
||||
var stringVar string
|
||||
var ptrVar *int
|
||||
|
||||
fmt.Printf("int: %d\n", intVar) // 0
|
||||
fmt.Printf("float: %f\n", floatVar) // 0.000000
|
||||
fmt.Printf("bool: %v\n", boolVar) // false
|
||||
fmt.Printf("string: '%s'\n", stringVar) // ''
|
||||
fmt.Printf("ptr: %v\n", ptrVar) // <nil>
|
||||
|
||||
// 零值判断
|
||||
if stringVar == "" {
|
||||
fmt.Println("字符串为空")
|
||||
}
|
||||
|
||||
if ptrVar == nil {
|
||||
fmt.Println("指针为空")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- 零值机制避免了"未初始化变量"的运行时错误
|
||||
- 在结构体中,未设置的字段自动为零值
|
||||
- 这是 Go 语言**安全性**的重要体现
|
||||
|
||||
```go
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
Email string
|
||||
}
|
||||
|
||||
func main() {
|
||||
u := User{} // 所有字段为零值
|
||||
fmt.Printf("%+v\n", u) // {Name: Age:0 Email:}
|
||||
|
||||
// 零值判断
|
||||
if u.Name == "" {
|
||||
fmt.Println("用户未设置名称")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2.3 核心数据类型详解
|
||||
|
||||
### 2.3.1 整型(Integer Types)
|
||||
|
||||
Go 提供了多种整型,适应不同场景:
|
||||
|
||||
```go
|
||||
var (
|
||||
a int8 // 8 位有符号整数,范围:-128 ~ 127
|
||||
b int16 // 16 位有符号整数,范围:-32768 ~ 32767
|
||||
c int32 // 32 位有符号整数,范围:-2147483648 ~ 2147483647
|
||||
d int64 // 64 位有符号整数,范围:-9223372036854775808 ~ 9223372036854775807
|
||||
|
||||
e uint8 // 8 位无符号整数,范围:0 ~ 255
|
||||
f uint16 // 16 位无符号整数,范围:0 ~ 65535
|
||||
g uint32 // 32 位无符号整数,范围:0 ~ 4294967295
|
||||
h uint64 // 64 位无符号整数,范围:0 ~ 18446744073709551615
|
||||
|
||||
i int // 平台相关:32 位系统为 int32,64 位系统为 int64
|
||||
j uint // 平台相关:32 位系统为 uint32,64 位系统为 uint64
|
||||
)
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `int` 和 `uint` 的大小取决于**平台架构**(32 位或 64 位)
|
||||
- 优先使用 `int` 和 `int64`,除非有特殊内存优化需求
|
||||
- `byte` 是 `uint8` 的别名,常用于处理二进制数据
|
||||
- `rune` 是 `int32` 的别名,用于表示 Unicode 字符
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var b byte = 'A' // byte 是 uint8 的别名
|
||||
var r rune = '中' // rune 是 int32 的别名,可存储 Unicode 字符
|
||||
|
||||
fmt.Printf("byte: %c, %d\n", b, b) // A, 65
|
||||
fmt.Printf("rune: %c, %d\n", r, r) // 中,20013
|
||||
|
||||
// 字节数测试
|
||||
fmt.Printf("len('A'): %d\n", len("A")) // 1
|
||||
fmt.Printf("len('中'): %d\n", len("中")) // 3(UTF-8 编码占 3 字节)
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3.2 浮点型(Floating-Point Types)
|
||||
|
||||
```go
|
||||
var (
|
||||
a float32 = 3.14
|
||||
b float64 = 3.141592653589793
|
||||
)
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `float64` 是**默认**浮点类型,精度更高
|
||||
- `float32` 节省内存,适合对精度要求不高的场景(如图形处理)
|
||||
- 浮点数比较时注意精度问题
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import "math"
|
||||
|
||||
func main() {
|
||||
a := 0.1 + 0.2
|
||||
b := 0.3
|
||||
|
||||
fmt.Printf("a == b: %v\n", a == b) // false(精度问题)
|
||||
fmt.Printf("a: %.20f\n", a) // 0.30000000000000004441
|
||||
|
||||
// 正确比较方式
|
||||
epsilon := 1e-9
|
||||
if math.Abs(a-b) < epsilon {
|
||||
fmt.Println("a 和 b 近似相等")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3.3 复数类型(Complex Types)
|
||||
|
||||
```go
|
||||
var (
|
||||
c1 complex64 = 1 + 2i
|
||||
c2 complex128 = 3.5 + 4.5i
|
||||
)
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `i` 或 `j` 表示虚部
|
||||
- 较少使用,主要用于科学计算和信号处理
|
||||
|
||||
### 2.3.4 布尔类型(Boolean Type)
|
||||
|
||||
```go
|
||||
var (
|
||||
isTrue bool = true
|
||||
isFalse bool = false
|
||||
)
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- 只有 `true` 和 `false` 两个值
|
||||
- **不能**与整数互相转换(`0` 不等于 `false`)
|
||||
- 逻辑运算符:`&&`(与)、`||`(或)、`!`(非)
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a, b := true, false
|
||||
|
||||
fmt.Printf("a && b: %v\n", a && b) // false
|
||||
fmt.Printf("a || b: %v\n", a || b) // true
|
||||
fmt.Printf("!a: %v\n", !a) // false
|
||||
|
||||
// 短路求值
|
||||
if false && someExpensiveFunction() {
|
||||
// someExpensiveFunction() 不会被执行
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3.5 字符串类型(String Type)
|
||||
|
||||
```go
|
||||
var s1 string = "Hello"
|
||||
var s2 string = "世界"
|
||||
var s3 string = `多行
|
||||
字符串`
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- 字符串是**不可变**的字节序列(UTF-8 编码)
|
||||
- 使用双引号 `"` 或反引号 `` ` ``
|
||||
- 反引号表示**原始字符串**,不转义任何字符
|
||||
- 字符串长度使用 `len()`,但返回的是**字节数**而非字符数
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := "Hello 世界"
|
||||
|
||||
fmt.Printf("len(s): %d\n", len(s)) // 11(字节数)
|
||||
fmt.Printf("s[0]: %c\n", s[0]) // H
|
||||
// fmt.Printf("s[6]: %c\n", s[6]) // 可能乱码(中文字符占 3 字节)
|
||||
|
||||
// 遍历字符串(按 rune)
|
||||
for i, r := range s {
|
||||
fmt.Printf("位置 %d: 字符 %c (Unicode: %d)\n", i, r, r)
|
||||
}
|
||||
|
||||
// 字符串拼接
|
||||
s1 := "Hello"
|
||||
s2 := "World"
|
||||
s3 := s1 + " " + s2
|
||||
|
||||
// 使用 strings.Builder(高性能拼接)
|
||||
var builder strings.Builder
|
||||
builder.WriteString("Hello")
|
||||
builder.WriteString(" ")
|
||||
builder.WriteString("World")
|
||||
fmt.Println(builder.String())
|
||||
}
|
||||
```
|
||||
|
||||
**注意**:需要导入 `strings` 包。
|
||||
|
||||
## 2.4 类型转换与类型推断
|
||||
|
||||
### 2.4.1 显式类型转换
|
||||
|
||||
Go **不支持**隐式类型转换,必须显式转换:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var a int = 42
|
||||
var b float64 = float64(a) // 显式转换
|
||||
var c int = int(b) // 显式转换
|
||||
|
||||
fmt.Printf("a: %d, type: %T\n", a, a)
|
||||
fmt.Printf("b: %f, type: %T\n", b, b)
|
||||
fmt.Printf("c: %d, type: %T\n", c, c)
|
||||
|
||||
// 错误示例:隐式转换(编译错误)
|
||||
// var x int = 3.14 // 编译错误!
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- 转换必须在**兼容类型**之间进行
|
||||
- 转换可能导致**精度丢失**或**溢出**
|
||||
- 使用 `int64(float64)` 转换时注意范围检查
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import "math"
|
||||
|
||||
func main() {
|
||||
var f float64 = 1.9
|
||||
var i int = int(f) // 截断小数部分,结果为 1
|
||||
|
||||
fmt.Printf("int(1.9): %d\n", i) // 1
|
||||
|
||||
// 四舍五入
|
||||
i = int(math.Round(f))
|
||||
fmt.Printf("round(1.9): %d\n", i) // 2
|
||||
|
||||
// 溢出检查
|
||||
var largeFloat float64 = 1e20
|
||||
var smallInt int = int(largeFloat) // 可能溢出
|
||||
fmt.Printf("largeFloat -> int: %d\n", smallInt)
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4.2 类型推断的边界
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
// 类型推断
|
||||
a := 10 // int
|
||||
b := 3.14 // float64
|
||||
c := "hello" // string
|
||||
d := true // bool
|
||||
|
||||
// 无法推断(需要显式类型)
|
||||
var e // 编译错误!必须提供类型或初始值
|
||||
|
||||
// 混合推断
|
||||
f, g, h := 1, 2.5, "test"
|
||||
fmt.Printf("f: %T, g: %T, h: %T\n", f, g, h)
|
||||
}
|
||||
```
|
||||
|
||||
## 2.5 运算符体系
|
||||
|
||||
### 2.5.1 算术运算符
|
||||
|
||||
```go
|
||||
a, b := 10, 3
|
||||
|
||||
// 加法、减法、乘法、除法、取余
|
||||
sum := a + b // 13
|
||||
diff := a - b // 7
|
||||
product := a * b // 30
|
||||
quotient := a / b // 3(整数除法)
|
||||
remainder := a % b // 1
|
||||
|
||||
// 注意:整数除法会截断小数部分
|
||||
fmt.Printf("10 / 3 = %d\n", 10/3) // 3
|
||||
fmt.Printf("10.0 / 3.0 = %f\n", 10.0/3.0) // 3.333333
|
||||
```
|
||||
|
||||
### 2.5.2 比较运算符
|
||||
|
||||
```go
|
||||
a, b := 10, 20
|
||||
|
||||
// 等于、不等于、大于、小于、大于等于、小于等于
|
||||
fmt.Println(a == b) // false
|
||||
fmt.Println(a != b) // true
|
||||
fmt.Println(a > b) // false
|
||||
fmt.Println(a < b) // true
|
||||
fmt.Println(a >= b) // false
|
||||
fmt.Println(a <= b) // true
|
||||
```
|
||||
|
||||
**注意**:字符串也可以比较(按字典序):
|
||||
```go
|
||||
fmt.Println("apple" < "banana") // true
|
||||
```
|
||||
|
||||
### 2.5.3 逻辑运算符
|
||||
|
||||
```go
|
||||
a, b := true, false
|
||||
|
||||
fmt.Println(a && b) // false(与)
|
||||
fmt.Println(a || b) // true(或)
|
||||
fmt.Println(!a) // false(非)
|
||||
```
|
||||
|
||||
**短路求值**:
|
||||
```go
|
||||
func expensive() bool {
|
||||
fmt.Println("expensive function called")
|
||||
return true
|
||||
}
|
||||
|
||||
if false && expensive() {
|
||||
// expensive() 不会被调用
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5.4 位运算符
|
||||
|
||||
```go
|
||||
a, b := 6, 3 // 6 = 110, 3 = 011
|
||||
|
||||
fmt.Printf("a & b: %d\n", a & b) // 2 (010)
|
||||
fmt.Printf("a | b: %d\n", a | b) // 7 (111)
|
||||
fmt.Printf("a ^ b: %d\n", a ^ b) // 5 (101)
|
||||
fmt.Printf("a &^ b: %d\n", a &^ b) // 4 (100) 位清除
|
||||
fmt.Printf("a << 1: %d\n", a << 1) // 12 (1100)
|
||||
fmt.Printf("a >> 1: %d\n", a >> 1) // 3 (011)
|
||||
```
|
||||
|
||||
### 2.5.5 赋值运算符
|
||||
|
||||
```go
|
||||
a := 10
|
||||
|
||||
a += 5 // a = a + 5
|
||||
a -= 3 // a = a - 3
|
||||
a *= 2 // a = a * 2
|
||||
a /= 4 // a = a / 4
|
||||
a %= 3 // a = a % 3
|
||||
|
||||
a &= 1 // a = a & 1
|
||||
a |= 2 // a = a | 2
|
||||
a ^= 4 // a = a ^ 4
|
||||
a <<= 1 // a = a << 1
|
||||
a >>= 1 // a = a >> 1
|
||||
```
|
||||
|
||||
## 2.6 流程控制
|
||||
|
||||
### 2.6.1 if-else 语句
|
||||
|
||||
```go
|
||||
score := 85
|
||||
|
||||
if score >= 90 {
|
||||
fmt.Println("优秀")
|
||||
} else if score >= 80 {
|
||||
fmt.Println("良好")
|
||||
} else if score >= 60 {
|
||||
fmt.Println("及格")
|
||||
} else {
|
||||
fmt.Println("不及格")
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `if` 条件前可以有一个**简短语句**
|
||||
- 简短语句的作用域仅限于 `if-else` 块
|
||||
|
||||
```go
|
||||
if x := computeValue(); x > 0 {
|
||||
fmt.Println("x 为正数:", x)
|
||||
} else {
|
||||
fmt.Println("x 为非正数:", x)
|
||||
}
|
||||
// fmt.Println(x) // 编译错误!x 在 if 块外不可见
|
||||
```
|
||||
|
||||
### 2.6.2 switch 语句
|
||||
|
||||
```go
|
||||
day := 3
|
||||
|
||||
switch day {
|
||||
case 1:
|
||||
fmt.Println("星期一")
|
||||
case 2:
|
||||
fmt.Println("星期二")
|
||||
case 3, 4, 5:
|
||||
fmt.Println("工作日")
|
||||
case 6, 7:
|
||||
fmt.Println("周末")
|
||||
default:
|
||||
fmt.Println("无效日期")
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `switch` 默认包含 `break`,不需要手动写
|
||||
- 可以使用 `fallthrough` 继续执行下一个 case
|
||||
- 支持**无表达式**的 switch(类似 if-else 链)
|
||||
|
||||
```go
|
||||
// 多值 case
|
||||
switch day {
|
||||
case 1, 2, 3, 4, 5:
|
||||
fmt.Println("工作日")
|
||||
case 6, 7:
|
||||
fmt.Println("周末")
|
||||
}
|
||||
|
||||
// fallthrough
|
||||
switch day {
|
||||
case 1:
|
||||
fmt.Println("星期一")
|
||||
fallthrough // 继续执行 case 2
|
||||
case 2:
|
||||
fmt.Println("星期二(包含星期一)")
|
||||
}
|
||||
|
||||
// 无表达式 switch
|
||||
switch {
|
||||
case score >= 90:
|
||||
fmt.Println("优秀")
|
||||
case score >= 80:
|
||||
fmt.Println("良好")
|
||||
default:
|
||||
fmt.Println("其他")
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6.3 for 循环
|
||||
|
||||
Go 只有 `for` 一种循环结构,但功能强大:
|
||||
|
||||
```go
|
||||
// 1. 传统 for 循环
|
||||
for i := 0; i < 5; i++ {
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
// 2. while 风格(省略初始化)
|
||||
i := 0
|
||||
for i < 5 {
|
||||
fmt.Println(i)
|
||||
i++
|
||||
}
|
||||
|
||||
// 3. 无限循环
|
||||
for {
|
||||
// ...
|
||||
break // 必须退出
|
||||
}
|
||||
|
||||
// 4. range 遍历
|
||||
slice := []int{1, 2, 3, 4, 5}
|
||||
for index, value := range slice {
|
||||
fmt.Printf("索引 %d: 值 %d\n", index, value)
|
||||
}
|
||||
|
||||
// 5. 只遍历索引
|
||||
for i := range slice {
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
// 6. 只遍历值(使用 _ 忽略索引)
|
||||
for _, v := range slice {
|
||||
fmt.Println(v)
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `range` 遍历字符串时,返回的是 **rune(Unicode 字符)** 而非字节
|
||||
- `for` 循环的初始化语句中的变量,作用域仅限于循环
|
||||
|
||||
```go
|
||||
// 字符串遍历
|
||||
s := "Hello"
|
||||
for i, r := range s {
|
||||
fmt.Printf("位置 %d: 字符 %c\n", i, r)
|
||||
}
|
||||
|
||||
// 作用域测试
|
||||
for i := 0; i < 3; i++ {
|
||||
fmt.Println(i)
|
||||
}
|
||||
// fmt.Println(i) // 编译错误!i 在循环外不可见
|
||||
```
|
||||
|
||||
### 2.6.4 break 和 continue
|
||||
|
||||
```go
|
||||
// break:跳出当前循环
|
||||
for i := 0; i < 10; i++ {
|
||||
if i == 5 {
|
||||
break
|
||||
}
|
||||
fmt.Println(i) // 输出 0-4
|
||||
}
|
||||
|
||||
// continue:跳过本次循环
|
||||
for i := 0; i < 10; i++ {
|
||||
if i%2 == 0 {
|
||||
continue
|
||||
}
|
||||
fmt.Println(i) // 输出 1, 3, 5, 7, 9
|
||||
}
|
||||
|
||||
// 带标签的 break/continue(跳出多层循环)
|
||||
outer:
|
||||
for i := 0; i < 3; i++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
if i == 1 && j == 1 {
|
||||
break outer // 跳出外层循环
|
||||
}
|
||||
fmt.Printf("i=%d, j=%d\n", i, j)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2.7 深度实践:综合案例
|
||||
|
||||
### 2.7.1 简易计算器
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var num1, num2 float64
|
||||
var operator string
|
||||
|
||||
fmt.Print("请输入第一个数字:")
|
||||
fmt.Scan(&num1)
|
||||
|
||||
fmt.Print("请输入运算符 (+, -, *, /):")
|
||||
fmt.Scan(&operator)
|
||||
|
||||
fmt.Print("请输入第二个数字:")
|
||||
fmt.Scan(&num2)
|
||||
|
||||
var result float64
|
||||
var valid bool
|
||||
|
||||
switch operator {
|
||||
case "+":
|
||||
result = num1 + num2
|
||||
valid = true
|
||||
case "-":
|
||||
result = num1 - num2
|
||||
valid = true
|
||||
case "*":
|
||||
result = num1 * num2
|
||||
valid = true
|
||||
case "/":
|
||||
if num2 == 0 {
|
||||
fmt.Println("错误:除数不能为零")
|
||||
valid = false
|
||||
} else {
|
||||
result = num1 / num2
|
||||
valid = true
|
||||
}
|
||||
default:
|
||||
fmt.Println("错误:无效的运算符")
|
||||
valid = false
|
||||
}
|
||||
|
||||
if valid {
|
||||
fmt.Printf("结果:%.2f %s %.2f = %.2f\n", num1, operator, num2, result)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.7.2 素数判断
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func isPrime(n int) bool {
|
||||
if n <= 1 {
|
||||
return false
|
||||
}
|
||||
if n == 2 {
|
||||
return true
|
||||
}
|
||||
if n%2 == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 只需检查到 sqrt(n)
|
||||
for i := 3; i*i <= n; i += 2 {
|
||||
if n%i == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("1 到 100 之间的素数:")
|
||||
count := 0
|
||||
for i := 1; i <= 100; i++ {
|
||||
if isPrime(i) {
|
||||
fmt.Printf("%d ", i)
|
||||
count++
|
||||
if count%10 == 0 {
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("\n共 %d 个素数\n", count)
|
||||
}
|
||||
```
|
||||
|
||||
## 2.8 常见陷阱与最佳实践
|
||||
|
||||
### 2.8.1 变量遮蔽陷阱
|
||||
|
||||
```go
|
||||
func shadowBug() {
|
||||
x := 10
|
||||
if true {
|
||||
x := 20 // 创建了新变量,遮蔽了外层 x
|
||||
fmt.Println(x) // 20
|
||||
}
|
||||
fmt.Println(x) // 10(外层 x 未变)
|
||||
|
||||
// 正确做法:使用 = 赋值
|
||||
if true {
|
||||
x = 30 // 修改外层 x
|
||||
fmt.Println(x) // 30
|
||||
}
|
||||
fmt.Println(x) // 30
|
||||
}
|
||||
```
|
||||
|
||||
### 2.8.2 整数除法陷阱
|
||||
|
||||
```go
|
||||
// 错误:整数除法
|
||||
result := 5 / 2 // 结果为 2,而非 2.5
|
||||
|
||||
// 正确:使用浮点数
|
||||
result := 5.0 / 2.0 // 结果为 2.5
|
||||
// 或
|
||||
result := float64(5) / float64(2)
|
||||
```
|
||||
|
||||
### 2.8.3 字符串长度陷阱
|
||||
|
||||
```go
|
||||
s := "你好"
|
||||
fmt.Println(len(s)) // 6(字节数,不是字符数)
|
||||
|
||||
// 正确获取字符数
|
||||
runeCount := len([]rune(s)) // 2
|
||||
fmt.Println(runeCount)
|
||||
```
|
||||
|
||||
### 2.8.4 最佳实践
|
||||
|
||||
1. **优先使用短声明 `:=`**,代码更简洁
|
||||
2. **利用零值**,减少不必要的初始化
|
||||
3. **避免变量遮蔽**,保持代码清晰
|
||||
4. **显式类型转换**,避免精度丢失
|
||||
5. **使用 `range` 遍历**,避免手动索引
|
||||
6. **注意字符串编码**,使用 `rune` 处理 Unicode
|
||||
|
||||
## 2.9 课后练习
|
||||
|
||||
1. **变量声明**:用四种方式声明变量,体会它们的区别
|
||||
2. **零值测试**:创建各种类型的未初始化变量,观察它们的零值
|
||||
3. **类型转换**:编写程序测试整数到浮点数的转换,观察精度变化
|
||||
4. **字符串遍历**:遍历包含中文的字符串,输出每个字符及其 Unicode 码点
|
||||
5. **素数统计**:统计 1 到 1000 之间的素数个数
|
||||
6. **综合练习**:编写一个温度转换器(摄氏度 ↔ 华氏度)
|
||||
|
||||
## 2.10 下一步
|
||||
|
||||
完成本章后,你将进入第三章:**数据结构详解**,深入学习数组、切片、映射和结构体,这是 Go 语言最核心的数据组织方式。
|
||||
|
||||
---
|
||||
|
||||
**代码仓库位置**:https://giter.top/openclaw/test/tree/main/chapters/chapter-2
|
||||
|
||||
**下一章预告**:数组与切片的区别、映射的底层原理、结构体嵌入与组合
|
||||
Reference in New Issue
Block a user