要点
条件决定走哪条路径,循环反复推进状态。先写循环不变量,例如“已经处理过的元素总和”,比先选择 for 写法更容易检查正确性。
条件语句
1. if-else 语句
基本语法
if condition {
// 条件为真时执行
} else {
// 条件为假时执行
}
简单示例
package main
import "fmt"
func checkAge(age int) {
if age >= 18 {
fmt.Printf("%d岁是成年人n", age)
} else {
fmt.Printf("%d岁是未成年人n", age)
}
}
func main() {
checkAge(16)
checkAge(18)
checkAge(25)
}
if-else if-else
func getGrade(score int) string {
if score >= 90 {
return "优秀"
} else if score >= 80 {
return "良好"
} else if score >= 70 {
return "中等"
} else if score >= 60 {
return "及格"
} else {
return "不及格"
}
}
func main() {
scores := []int{95, 85, 75, 65, 55}
for _, score := range scores {
fmt.Printf("分数: %d, 等级: %sn", score, getGrade(score))
}
}
2. 带初始化的 if 语句
func main() {
// if语句中的短变量声明
if x := 10; x > 5 {
fmt.Printf("x = %d, 大于5n", x)
}
// if-else中的短变量声明
if name := "张三"; name != "" {
fmt.Printf("姓名: %sn", name)
} else {
fmt.Println("姓名为空")
}
}
switch 语句
1. 基本 switch
func dayOfWeek(day int) string {
switch day {
case 1:
return "星期一"
case 2:
return "星期二"
case 3:
return "星期三"
case 4:
return "星期四"
case 5:
return "星期五"
case 6:
return "星期六"
case 7:
return "星期日"
default:
return "无效的日期"
}
}
func main() {
for i := 1; i <= 8; i++ {
fmt.Printf("第%d天是%sn", i, dayOfWeek(i))
}
}
2. switch 的表达式形式
func checkNumber(num int) string {
switch {
case num < 0:
return "负数"
case num == 0:
return "零"
case num > 0 && num < 10:
return "个位数"
case num >= 10 && num < 100:
return "两位数"
default:
return "多位数"
}
}
func main() {
numbers := []int{-5, 0, 7, 15, 123}
for _, num := range numbers {
fmt.Printf("%d是%sn", num, checkNumber(num))
}
}
3. fallthrough 的使用
func checkTrafficLight(light string) {
fmt.Printf("交通灯: %s - ", light)
switch light {
case "红灯":
fmt.Println("停止")
case "黄灯":
fmt.Println("准备")
fallthrough
case "绿灯":
fmt.Println("通行")
default:
fmt.Println("无效信号")
}
}
func main() {
lights := []string{"红灯", "黄灯", "绿灯", "蓝灯"}
for _, light := range lights {
checkTrafficLight(light)
}
}
4. 类型 switch
func printType(value interface{}) {
switch v := value.(type) {
case int:
fmt.Printf("整数: %dn", v)
case string:
fmt.Printf("字符串: %sn", v)
case bool:
fmt.Printf("布尔值: %tn", v)
case []int:
fmt.Printf("整数数组: %vn", v)
default:
fmt.Printf("未知类型: %Tn", v)
}
}
func main() {
values := []interface{}{42, "Hello", true, []int{1, 2, 3}, 3.14}
for _, value := range values {
printType(value)
}
}
循环结构
1. for 循环 – 三种形式
标准 for 循环
func printNumbers(n int) {
fmt.Printf("1到%d的数字:n", n)
for i := 1; i <= n; i++ {
fmt.Printf("%d ", i)
}
fmt.Println()
}
func main() {
printNumbers(5)
}
while 风格的 for 循环
func readInputUntilStop() {
var input string
count := 0
for {
fmt.Scanln(&input)
if input == "stop" {
break
}
count++
fmt.Printf("输入 %d: %sn", count, input)
}
fmt.Printf("共输入了%d个有效输入n", count)
}
func main() {
fmt.Println("输入内容,输入'stop'结束:")
readInputUntilStop()
}
无限 for 循环
func main() {
// 无限循环(需要break退出)
count := 0
for {
fmt.Printf("计数: %dn", count)
count++
if count >= 5 {
break
}
}
fmt.Println("循环结束")
}
2. range 循环
遍历数组/切片
func main() {
numbers := []int{10, 20, 30, 40, 50}
// 遍历数组元素和索引
fmt.Println("遍历数组元素:")
for i, num := range numbers {
fmt.Printf("索引 %d: %dn", i, num)
}
// 只遍历元素
fmt.Println("n只遍历元素:")
for _, num := range numbers {
fmt.Printf("%d ", num)
}
fmt.Println()
}
遍历映射
func main() {
scores := map[string]int{
"张三": 85,
"李四": 92,
"王五": 78,
}
// 遍历映射的键值对
fmt.Println("学生成绩:")
for name, score := range scores {
fmt.Printf("%s: %d分n", name, score)
}
}
遍历字符串
func main() {
text := "Hello, 世界!"
// 遍历字符串的字节(UTF-8编码)
fmt.Println("字节遍历:")
for i, b := range []byte(text) {
fmt.Printf("位置 %d: %cn", i, b)
}
// 遍历字符串的字符(Unicode码点)
fmt.Println("n字符遍历:")
for i, r := range text {
fmt.Printf("位置 %d: %c (码点: %d)n", i, r, r)
}
}
3. 嵌套循环
多重循环
func printMultiplicationTable(n int) {
fmt.Printf("%d乘法表:n", n)
for i := 1; i <= n; i++ {
for j := 1; j <= n; j++ {
fmt.Printf("%dx%d=%-3d ", i, j, i*j)
}
fmt.Println()
}
}
func main() {
printMultiplicationTable(5)
}
循环中的标签和 break/continue
func searchInMatrix(matrix [][]int, target int) (row, col int, found bool) {
OuterLoop:
for r, rowSlice := range matrix {
for c, value := range rowSlice {
if value == target {
return r, c, true
}
if value > target {
// 跳出当前行循环,继续下一行
continue OuterLoop
}
}
}
return -1, -1, false
}
func main() {
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{10, 11, 12},
}
row, col, found := searchInMatrix(matrix, 8)
if found {
fmt.Printf("找到目标值8在位置[%d][%d]n", row, col)
} else {
fmt.Println("未找到目标值")
}
}
defer 语句
1. defer 的基本用法
func demonstrateDefer() {
fmt.Println("函数开始")
defer fmt.Println("函数结束 (1)")
defer fmt.Println("函数结束 (2)")
defer fmt.Println("函数结束 (3)")
fmt.Println("函数执行中...")
}
func main() {
demonstrateDefer()
fmt.Println("main函数继续执行")
}
输出顺序:
函数开始
函数执行中...
函数结束 (3)
函数结束 (2)
函数结束 (1)
main函数继续执行
2. defer 的实际应用
资源清理
import "os"
func processFile(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close() // 确保文件被关闭
// 读取文件内容
content := make([]byte, 1024)
n, err := file.Read(content)
if err != nil {
return err
}
fmt.Printf("读取了%d个字节n", n)
return nil
}
func main() {
err := processFile("example.txt")
if err != nil {
fmt.Printf("处理文件错误: %vn", err)
}
}
panic 恢复
func safeOperation() (result string) {
defer func() {
if r := recover(); r != nil {
result = fmt.Sprintf("操作失败,已恢复: %v", r)
}
}()
// 模拟可能失败的操作
// panic("模拟错误")
result = "操作成功"
return
}
func main() {
result := safeOperation()
fmt.Printf("结果: %sn", result)
}
实际应用示例
示例1: 用户输入验证
package main
import (
"bufio"
"fmt"
"io"
"unicode/utf8"
"os"
"strings"
)
func validateInput(input string) (bool, string) {
// 去除前后空格
input = strings.TrimSpace(input)
if input == "" {
return false, "输入不能为空"
}
if utf8.RuneCountInString(input) > 100 {
return false, "输入过长(最多100字符)"
}
return true, "输入有效"
}
func getUserInput(reader *bufio.Reader, prompt string) (string, error) {
fmt.Print(prompt)
input, err := reader.ReadString('n')
if err != nil && len(input) == 0 { return "", err }
return strings.TrimSpace(input), nil
}
func main() {
fmt.Println("=== 用户输入验证系统 ===")
reader := bufio.NewReader(os.Stdin) // 复用缓冲区,避免丢失已预读的下一行
for {
input, err := getUserInput(reader, "请输入内容(输入'quit'退出): ")
if err != nil {
if err != io.EOF { fmt.Fprintln(os.Stderr, "读取失败:", err) }
break // 关闭输入时退出,不能把 EOF 当空字符串反复重试
}
if input == "quit" {
break
}
valid, message := validateInput(input)
if valid {
fmt.Printf("✅ %sn", message)
} else {
fmt.Printf("❌ %sn", message)
}
}
fmt.Println("程序结束")
}
示例2: 菜单驱动程序
package main
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"
)
func displayMenu() {
fmt.Println("=== 计算器菜单 ===")
fmt.Println("1. 加法n2. 减法n3. 乘法n4. 除法n5. 退出")
fmt.Print("请选择操作 (1-5): ")
}
func readNumber(scanner *bufio.Scanner, prompt string) (float64, error) {
fmt.Print(prompt)
if !scanner.Scan() {
if err := scanner.Err(); err != nil { return 0, err }
return 0, io.EOF
}
return strconv.ParseFloat(strings.TrimSpace(scanner.Text()), 64)
}
func getNumbers(scanner *bufio.Scanner) (float64, float64, error) {
a, err := readNumber(scanner, "请输入第一个数字: ")
if err != nil { return 0, 0, err }
b, err := readNumber(scanner, "请输入第二个数字: ")
return a, b, err
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
for {
displayMenu()
if !scanner.Scan() {
if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, err) }
break // 输入流已关闭;不会进入无休止的菜单循环
}
choice, err := strconv.Atoi(strings.TrimSpace(scanner.Text()))
if err != nil || choice < 1 || choice > 5 {
fmt.Println("无效选择,请输入1到5")
continue
}
if choice == 5 {
fmt.Println("感谢使用,再见!")
break
}
a, b, err := getNumbers(scanner)
if err != nil {
if err == io.EOF { break }
fmt.Println("数字无效,请重新选择操作:", err)
continue
}
switch choice {
case 1:
fmt.Printf("%.2f + %.2f = %.2fn", a, b, a+b)
case 2:
fmt.Printf("%.2f - %.2f = %.2fn", a, b, a-b)
case 3:
fmt.Printf("%.2f × %.2f = %.2fn", a, b, a*b)
case 4:
if b == 0 { fmt.Println("错误:除数不能为0") } else {
fmt.Printf("%.2f ÷ %.2f = %.2fn", a, b, a/b)
}
}
}
}
示例3: 数据处理管道
package main
import (
"fmt"
)
// 过滤函数
func filter(numbers []int, predicate func(int) bool) []int {
var result []int
for _, num := range numbers {
if predicate(num) {
result = append(result, num)
}
}
return result
}
// 映射函数
func mapNumbers(numbers []int, transform func(int) int) []int {
result := make([]int, len(numbers))
for i, num := range numbers {
result[i] = transform(num)
}
return result
}
// 规约函数
func reduce(numbers []int, operation func(int, int) int) int {
if len(numbers) == 0 {
return 0
}
result := numbers[0]
for _, num := range numbers[1:] {
result = operation(result, num)
}
return result
}
// 数据处理管道
func processNumbers(numbers []int) []int {
// 管道: 过滤偶数 → 平方 → 求和
pipeline := func(nums []int) []int {
// 步骤1: 过滤偶数
evens := filter(nums, func(n int) bool {
return n%2 == 0
})
// 步骤2: 平方每个偶数
squares := mapNumbers(evens, func(n int) int {
return n * n
})
// 步骤3: 计算总和
total := reduce(squares, func(a, b int) int {
return a + b
})
return []int{total}
}
return pipeline(numbers)
}
func main() {
data := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Printf("原始数据: %vn", data)
result := processNumbers(data)
fmt.Printf("偶数平方和: %vn", result)
// 验证结果
sum := 0
for _, num := range data {
if num%2 == 0 {
sum += num * num
}
}
fmt.Printf("验证结果: %dn", sum)
}
常见错误和注意事项
1. 无限循环
// ❌ 错误 - 忘须有退出条件
func badLoop() {
count := 0
for {
count++
// 没有break或return,会无限循环
}
}
// ✅ 正确 - 有明确的退出条件
func goodLoop(n int) {
for i := 0; i < n; i++ {
// 循环条件明确
}
}
2. 误以为 switch 缺少 break(沿用 C 直觉)
与 C 不同,Go 的每个 case 执行完会自动结束(隐式 break),不会贯穿到下一个 case。从 C/C++/Java 转来的初学者常误标”没有 break”为错误——其实下面这段代码本身就是正确的:
// ✅ 正确 - Go 的 case 隐式 break,匹配 "B" 只返回 "良好",不会贯穿到 "C"
func checkGrade(grade string) string {
switch grade {
case "A":
return "优秀"
case "B":
return "良好"
case "C":
return "及格"
}
return "需要改进"
}
反过来,Go 用 fallthrough 显式声明贯穿:执行完本 case 后强制进入下一个 case 的语句体(不再判断下一个 case 的条件)。只在确实需要贯穿相邻分支时使用:
// fallthrough 示例:n=5 时输出 "个位数" 和 "正数"(跳过对 n > 0 的条件判断)
switch n := 5; {
case n < 10:
fmt.Println("个位数")
fallthrough
case n > 0:
fmt.Println("正数")
default:
fmt.Println("其他")
}
3. defer 在循环中的使用
// ❌ 谨试时的陷阱
func badDeferInLoop() {
for i := 0; i < 3; i++ {
defer fmt.Printf("循环结束: %dn", i)
// 所有defer会在函数结束时执行,不是每次循环结束时
}
}
// ✅ 正确的方式 - 匿名函数创建defer
func goodDeferInLoop() {
for i := 0; i < 3; i++ {
func(id int) {
defer fmt.Printf("循环结束: %dn", id)
}(i)
}
}
性能优化提示
1. 循环优化
// ❌ 效率低 - 每次都计算长度
func slowSum(numbers []int) int {
sum := 0
for i := 0; i < len(numbers); i++ {
sum += numbers[i] // 每次都调用len()
}
return sum
}
// ✅ 效率高 - 预先计算长度
func fastSum(numbers []int) int {
sum := 0
length := len(numbers) // 预先计算
for i := 0; i < length; i++ {
sum += numbers[i]
}
return sum
}
2. 避免不必要的循环
// ❌ 不必要的循环
func containsValue(slice []int, target int) bool {
for _, v := range slice {
if v == target {
return true
}
}
return false
}
// ✅ 使用map查找(适合大数据集)
func containsValueMap(set map[int]struct{}) bool {
_, exists := set[target]
return exists
}
可运行的控制流契约:先写出每个分支的结果
前置知识是函数、切片和 fmt。阅读较长的 if、switch 或循环时,先把输入和预期输出列出来;这会迫使我们同时覆盖正常路径、跳过路径和提前退出路径。下面是一个完整程序:它用 continue 跳过负数、用 break 在遇到 0 时停止,并用无表达式 switch 给最终状态分类。break 只结束最近的一层 for;若有嵌套循环,需要标签才可以结束外层循环。
将其保存为 main.go 后执行 go run main.go:
package main
import "fmt"
func summarize(values []int) (sum int, skipped int, stopped bool) {
for _, value := range values {
switch {
case value < 0:
skipped++
continue
case value == 0:
stopped = true
break
default:
sum += value
}
if stopped {
break
}
}
return sum, skipped, stopped
}
func main() {
sum, skipped, stopped := summarize([]int{2, -1, 3, 0, 99})
fmt.Printf("sum=%d skipped=%d stopped=%tn", sum, skipped, stopped)
switch {
case sum >= 5 && stopped:
fmt.Println("result=complete-before-sentinel")
default:
fmt.Println("result=unexpected")
}
}
输出必须是 sum=5 skipped=1 stopped=true 与 result=complete-before-sentinel。continue 立刻开始下一次迭代,所以负数既不加入总和也不会触发停止;break 从 switch 退出后仍会执行紧随其后的检查,然后由该检查结束 for。这里特意没有把 99 计入总和,作为“停止确实生效”的可观察证据。实际业务中,若 0 是合法数据而不是哨兵值,应把停止条件做成单独的布尔结果、错误或显式长度,避免让数据值兼任控制信号。
文档交叉引用
相关文档
参考资源
- 📖 [Go 控制流文档]: https://golang.org/ref/spec#Statements
- 📖 [Go for 循环文档]: https://golang.org/ref/spec#For_statements
- 📖 [Go switch 文档]: https://golang.org/ref/spec#Switch_statements
总结
核心要点回顾
- 条件语句: 掌握 if-else 和 switch 的使用
- 循环结构: 理解 for 循环的各种形式和 range 循环
- defer 语句: 学会使用 defer 进行资源清理
- 控制流程: 组合使用各种控制结构
实践练习
- [ ] 编写包含多种条件判断的程序
- [ ] 使用 switch 替代复杂的 if-else 链
- [ ] 实现嵌套循环和标签控制
- [ ] 使用 defer 处理资源清理
- [ ] 创建交互式菜单驱动程序
文档状态: ✅ 已完成最后更新: 2026年9月版本: v1.0.0
💡 学习建议: – 理解 Go 控制流程的简洁性和安全性 – 善用 switch 语句替代复杂的 if-else 结构 – 掌握 range 循环,避免手动的索引管理 – 使用 defer 确保资源的正确释放 – 避免无限循环,始终提供明确的退出条件