Skip to content

Go语言控制结构

本文档详细介绍Go语言的控制结构,包括条件语句、循环语句和跳转语句等流程控制机制。

📋 目录

控制结构概述

Go语言提供了三种基本的控制结构:

  1. 顺序控制 - 程序按顺序执行
  2. 分支控制 - 根据条件选择执行路径
  3. 循环控制 - 重复执行代码块

程序执行流程

go
package main

import "fmt"

func main() {
    // 1. 顺序执行
    fmt.Println("步骤1")
    fmt.Println("步骤2")
    
    // 2. 分支控制
    x := 10
    if x > 5 {
        fmt.Println("x大于5")
    }
    
    // 3. 循环控制
    for i := 0; i < 3; i++ {
        fmt.Printf("循环第%d\n", i+1)
    }
}

条件语句

if 语句

基本语法

go
// 基本if语句
if condition {
    // 条件为true时执行
}

// if-else语句
if condition {
    // 条件为true时执行
} else {
    // 条件为false时执行
}

// if-else if-else语句
if condition1 {
    // condition1为true时执行
} else if condition2 {
    // condition2为true时执行
} else {
    // 所有条件都为false时执行
}

实际示例

go
func main() {
    age := 18
    
    // 简单if语句
    if age >= 18 {
        fmt.Println("已成年")
    }
    
    // if-else语句
    if age >= 60 {
        fmt.Println("老年人")
    } else if age >= 18 {
        fmt.Println("成年人")
    } else {
        fmt.Println("未成年人")
    }
    
    // 多条件判断
    score := 85
    if score >= 90 {
        fmt.Println("优秀")
    } else if score >= 80 {
        fmt.Println("良好")
    } else if score >= 70 {
        fmt.Println("中等")
    } else if score >= 60 {
        fmt.Println("及格")
    } else {
        fmt.Println("不及格")
    }
}

if语句的特殊用法

1. 带初始化语句的if
go
// 在if语句中声明变量
if num := 42; num > 0 {
    fmt.Printf("正数: %d\n", num)
}
// num在此处不可访问

// 实际应用:错误处理
if err := someFunction(); err != nil {
    fmt.Printf("错误: %v\n", err)
    return
}

// 文件操作示例
if file, err := os.Open("test.txt"); err != nil {
    fmt.Printf("打开文件失败: %v\n", err)
} else {
    defer file.Close()
    fmt.Println("文件打开成功")
}
2. 逻辑运算符
go
func main() {
    age := 25
    hasLicense := true
    
    // 逻辑与 (&&)
    if age >= 18 && hasLicense {
        fmt.Println("可以开车")
    }
    
    // 逻辑或 (||)
    if age < 18 || !hasLicense {
        fmt.Println("不能开车")
    }
    
    // 逻辑非 (!)
    if !hasLicense {
        fmt.Println("没有驾照")
    }
    
    // 复杂条件
    income := 50000
    if (age >= 18 && age <= 65) && (income > 30000 || hasLicense) {
        fmt.Println("符合贷款条件")
    }
}
3. 短路求值
go
func expensiveOperation() bool {
    fmt.Println("执行昂贵操作")
    return true
}

func main() {
    x := 5
    
    // 短路与:如果第一个条件为false,不会执行第二个条件
    if x > 10 && expensiveOperation() {
        fmt.Println("条件成立")
    }
    // expensiveOperation不会被调用
    
    // 短路或:如果第一个条件为true,不会执行第二个条件
    if x > 0 || expensiveOperation() {
        fmt.Println("条件成立")
    }
    // expensiveOperation不会被调用
}

循环语句

Go语言只有一种循环语句:for,但它有多种形式。

for 循环的形式

1. 标准for循环

go
// 基本语法
for 初始化; 条件; 后置语句 {
    // 循环体
}

// 示例
func main() {
    // 基本循环
    for i := 0; i < 5; i++ {
        fmt.Printf("i = %d\n", i)
    }
    
    // 嵌套循环
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            fmt.Printf("(%d,%d) ", i, j)
        }
        fmt.Println()
    }
}

2. while风格循环

go
func main() {
    // 省略初始化和后置语句
    i := 0
    for i < 5 {
        fmt.Printf("i = %d\n", i)
        i++
    }
    
    // 无限循环
    count := 0
    for {
        fmt.Printf("无限循环 %d\n", count)
        count++
        if count >= 3 {
            break  // 跳出循环
        }
    }
}

3. range循环

range 用于遍历数组、切片、字符串、映射和通道:

遍历数组和切片
go
func main() {
    // 遍历数组
    arr := [5]int{10, 20, 30, 40, 50}
    
    // 获取索引和值
    for index, value := range arr {
        fmt.Printf("arr[%d] = %d\n", index, value)
    }
    
    // 只获取索引
    for index := range arr {
        fmt.Printf("索引: %d\n", index)
    }
    
    // 只获取值
    for _, value := range arr {
        fmt.Printf("值: %d\n", value)
    }
    
    // 遍历切片
    slice := []string{"Go", "Python", "Java"}
    for i, lang := range slice {
        fmt.Printf("%d: %s\n", i, lang)
    }
}
遍历字符串
go
func main() {
    str := "Hello世界"
    
    // 按字节遍历
    fmt.Println("按字节遍历:")
    for i := 0; i < len(str); i++ {
        fmt.Printf("%d: %c\n", i, str[i])
    }
    
    // 按Unicode字符遍历
    fmt.Println("按Unicode字符遍历:")
    for index, char := range str {
        fmt.Printf("%d: %c\n", index, char)
    }
}
遍历映射
go
func main() {
    m := map[string]int{
        "apple":  5,
        "banana": 3,
        "orange": 8,
    }
    
    // 遍历键值对
    for key, value := range m {
        fmt.Printf("%s: %d\n", key, value)
    }
    
    // 只遍历键
    for key := range m {
        fmt.Printf("键: %s\n", key)
    }
    
    // 只遍历值
    for _, value := range m {
        fmt.Printf("值: %d\n", value)
    }
}
遍历通道
go
func main() {
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)
    
    // 遍历通道中的值
    for value := range ch {
        fmt.Printf("接收到: %d\n", value)
    }
}

循环控制语句

break 语句

go
func main() {
    // 跳出单层循环
    for i := 0; i < 10; i++ {
        if i == 5 {
            break  // 跳出循环
        }
        fmt.Printf("i = %d\n", i)
    }
    
    // 跳出嵌套循环 - 使用标签
outer:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            if i == 1 && j == 1 {
                break outer  // 跳出外层循环
            }
            fmt.Printf("(%d,%d) ", i, j)
        }
    }
    fmt.Println("\n循环结束")
}

continue 语句

go
func main() {
    // 跳过当前迭代
    for i := 0; i < 10; i++ {
        if i%2 == 0 {
            continue  // 跳过偶数
        }
        fmt.Printf("奇数: %d\n", i)
    }
    
    // 嵌套循环中的continue
outer:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            if j == 1 {
                continue outer  // 跳到外层循环的下一次迭代
            }
            fmt.Printf("(%d,%d) ", i, j)
        }
    }
}

选择语句

switch 语句

基本switch

go
func main() {
    day := 3
    
    switch day {
    case 1:
        fmt.Println("星期一")
    case 2:
        fmt.Println("星期二")
    case 3:
        fmt.Println("星期三")
    case 4:
        fmt.Println("星期四")
    case 5:
        fmt.Println("星期五")
    case 6, 7:  // 多个值
        fmt.Println("周末")
    default:
        fmt.Println("无效的日期")
    }
}

表达式switch

go
func main() {
    score := 85
    
    switch {
    case score >= 90:
        fmt.Println("优秀")
    case score >= 80:
        fmt.Println("良好")
    case score >= 70:
        fmt.Println("中等")
    case score >= 60:
        fmt.Println("及格")
    default:
        fmt.Println("不及格")
    }
}

带初始化的switch

go
func main() {
    switch hour := time.Now().Hour(); {
    case hour < 6:
        fmt.Println("凌晨")
    case hour < 12:
        fmt.Println("上午")
    case hour < 18:
        fmt.Println("下午")
    default:
        fmt.Println("晚上")
    }
}

fallthrough 语句

go
func main() {
    grade := 'B'
    
    switch grade {
    case 'A':
        fmt.Println("优秀")
        fallthrough  // 继续执行下一个case
    case 'B':
        fmt.Println("良好")
        fallthrough
    case 'C':
        fmt.Println("及格")
    case 'D':
        fmt.Println("不及格")
    default:
        fmt.Println("无效等级")
    }
}

类型switch

go
func processValue(value interface{}) {
    switch v := value.(type) {
    case int:
        fmt.Printf("整数: %d\n", v)
    case string:
        fmt.Printf("字符串: %s\n", v)
    case bool:
        fmt.Printf("布尔值: %t\n", v)
    case []int:
        fmt.Printf("整数切片: %v\n", v)
    default:
        fmt.Printf("未知类型: %T\n", v)
    }
}

func main() {
    processValue(42)
    processValue("Hello")
    processValue(true)
    processValue([]int{1, 2, 3})
    processValue(3.14)
}

select 语句

select 用于处理多个通道操作:

go
func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "来自ch1的消息"
    }()
    
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "来自ch2的消息"
    }()
    
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("接收到:", msg1)
        case msg2 := <-ch2:
            fmt.Println("接收到:", msg2)
        case <-time.After(3 * time.Second):
            fmt.Println("超时")
            return
        }
    }
}

跳转语句

goto 语句

虽然Go支持goto,但不推荐使用:

go
func main() {
    i := 0
    
start:
    if i < 5 {
        fmt.Printf("i = %d\n", i)
        i++
        goto start
    }
    
    fmt.Println("循环结束")
}

标签的使用

标签主要用于break和continue:

go
func main() {
    // 查找二维数组中的特定值
    matrix := [][]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }
    
    target := 5
    found := false
    
search:
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            if matrix[i][j] == target {
                fmt.Printf("找到 %d 在位置 (%d, %d)\n", target, i, j)
                found = true
                break search  // 跳出所有循环
            }
        }
    }
    
    if !found {
        fmt.Printf("未找到 %d\n", target)
    }
}

错误处理

基本错误处理模式

go
import (
    "errors"
    "fmt"
)

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除数不能为0")
    }
    return a / b, nil
}

func main() {
    // 标准错误处理
    result, err := divide(10, 2)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
        return
    }
    fmt.Printf("结果: %.2f\n", result)
    
    // 处理错误情况
    result, err = divide(10, 0)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
        // 可以选择返回、继续或其他处理方式
    }
}

多重错误处理

go
func processFile(filename string) error {
    // 打开文件
    file, err := os.Open(filename)
    if err != nil {
        return fmt.Errorf("打开文件失败: %w", err)
    }
    defer file.Close()
    
    // 读取文件
    data := make([]byte, 1024)
    n, err := file.Read(data)
    if err != nil && err != io.EOF {
        return fmt.Errorf("读取文件失败: %w", err)
    }
    
    // 处理数据
    fmt.Printf("读取了 %d 字节\n", n)
    return nil
}

func main() {
    if err := processFile("test.txt"); err != nil {
        fmt.Printf("处理文件时出错: %v\n", err)
    }
}

错误处理最佳实践

1. 及早返回

go
func validateUser(user User) error {
    if user.Name == "" {
        return errors.New("用户名不能为空")
    }
    
    if user.Age < 0 {
        return errors.New("年龄不能为负数")
    }
    
    if user.Email == "" {
        return errors.New("邮箱不能为空")
    }
    
    // 所有验证通过
    return nil
}

2. 错误包装

go
func readConfig(filename string) (*Config, error) {
    data, err := ioutil.ReadFile(filename)
    if err != nil {
        return nil, fmt.Errorf("读取配置文件 %s 失败: %w", filename, err)
    }
    
    var config Config
    if err := json.Unmarshal(data, &config); err != nil {
        return nil, fmt.Errorf("解析配置文件 %s 失败: %w", filename, err)
    }
    
    return &config, nil
}

3. 自定义错误类型

go
type ValidationError struct {
    Field   string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("验证失败 - %s: %s", e.Field, e.Message)
}

func validateAge(age int) error {
    if age < 0 {
        return ValidationError{
            Field:   "age",
            Message: "年龄不能为负数",
        }
    }
    if age > 150 {
        return ValidationError{
            Field:   "age", 
            Message: "年龄不能超过150",
        }
    }
    return nil
}

实际应用示例

go
func main() {
    // 用户输入处理
    var input string
    fmt.Print("请输入一个数字: ")
    fmt.Scanln(&input)
    
    // 转换并验证输入
    num, err := strconv.Atoi(input)
    if err != nil {
        fmt.Printf("输入无效: %v\n", err)
        return
    }
    
    // 根据数字范围进行不同处理
    switch {
    case num < 0:
        fmt.Println("负数")
    case num == 0:
        fmt.Println("零")
    case num > 0 && num <= 10:
        fmt.Println("小正数")
    case num > 10 && num <= 100:
        fmt.Println("中等正数")
    default:
        fmt.Println("大正数")
    }
    
    // 计算阶乘
    if num >= 0 && num <= 20 {
        factorial := 1
        for i := 1; i <= num; i++ {
            factorial *= i
        }
        fmt.Printf("%d的阶乘是: %d\n", num, factorial)
    } else if num > 20 {
        fmt.Println("数字太大,无法计算阶乘")
    }
}

通过掌握这些控制结构,您就可以编写具有复杂逻辑的Go程序了。控制结构是编程的基础,合理使用它们可以让程序逻辑清晰、易于维护。

基于 MIT 许可发布