Go 语言控制结构

Go 语言控制结构

条件语句

1. if 语句

// 基本 if 语句
if x > 0 {
    fmt.Println("x 大于 0")
}

// if 语句带初始化
if x := 10; x > 0 {
    fmt.Println("x 大于 0")
}

// if-else 语句
if x > 0 {
    fmt.Println("x 大于 0")
} else {
    fmt.Println("x 小于等于 0")
}

// if-else if-else 语句
if x > 0 {
    fmt.Println("x 大于 0")
} else if x < 0 {
    fmt.Println("x 小于 0")
} else {
    fmt.Println("x 等于 0")
}

2. switch 语句

switch 语句是多分支条件语句,比多个 if-else 语句更加清晰和高效。Go 语言的 switch 比其他语言更加灵活,不需要显式的 break 语句来阻止执行下一个 case。

特点:

  • 自动 break:每个 case 执行完后会自动退出,不会继续执行下一个 case
  • 多条件匹配:一个 case 可以匹配多个值,用逗号分隔
  • 表达式匹配:case 可以是表达式而不仅仅是常量
  • fallthrough:可以使用 fallthrough 关键字强制执行下一个 case
  • 类型 switch:可以用于检查接口变量的具体类型

基本语法:

// 基本 switch 语句
switch x {
case 1:
    fmt.Println("x 等于 1")
case 2:
    fmt.Println("x 等于 2")
default:
    fmt.Println("x 不等于 1 或 2")
}
// 多条件匹配示例
switch day := "星期六"; day {
case "星期六", "星期日":
    fmt.Println("周末")
case "星期一", "星期二", "星期三", "星期四", "星期五":
    fmt.Println("工作日")
default:
    fmt.Println("无效的日期")
}

// 类型 switch 示例
func typeCheck(x interface{}) {
    switch v := x.(type) {
    case nil:
        fmt.Println("x 是 nil")
    case int:
        fmt.Printf("x 是整数: %d\n", v)
    case float64:
        fmt.Printf("x 是浮点数: %f\n", v)
    case string:
        fmt.Printf("x 是字符串: %s\n", v)
    case bool:
        fmt.Printf("x 是布尔值: %t\n", v)
    case []int:
        fmt.Printf("x 是整数切片: %v\n", v)
    case map[string]int:
        fmt.Printf("x 是字符串到整数的映射: %v\n", v)
    default:
        fmt.Printf("x 是未知类型: %T\n", v)
    }
}

// switch 语句带初始化
switch x := 10; x {
case 1:
    fmt.Println("x 等于 1")
case 2:
    fmt.Println("x 等于 2")
default:
    fmt.Println("x 不等于 1 或 2")
}

// 无条件的 switch 语句
switch {
case x > 0:
    fmt.Println("x 大于 0")
case x < 0:
    fmt.Println("x 小于 0")
default:
    fmt.Println("x 等于 0")
}

// fallthrough 语句
switch x {
case 1:
    fmt.Println("x 等于 1")
    fallthrough  // 使用fallthrough会强制执行下一个case,而不会判断下一个case的条件是否满足
    // 如果有case 3且case 2也有fallthrough,则会继续执行case 3
    // fallthrough只会执行紧跟着的下一个case,除非每个case都使用fallthrough
case 2:
    fmt.Println("x 等于 2")
}

循环语句

1. for 循环

// 基本 for 循环
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

// 类似 while 的 for 循环
i := 0
for i < 10 {
    fmt.Println(i)
    i++
}

// 无限循环
for {
    fmt.Println("无限循环")
    break  // 跳出循环
}

// 带 range 的 for 循环
arr := []int{1, 2, 3, 4, 5}
for index, value := range arr {
    fmt.Printf("索引:%d,值:%d\n", index, value)
}

// 遍历字符串
str := "Hello"
for index, char := range str {
    fmt.Printf("索引:%d,字符:%c\n", index, char)
}

// 遍历映射
m := map[string]int{"a": 1, "b": 2}
for key, value := range m {
    fmt.Printf("键:%s,值:%d\n", key, value)
}

2. break 和 continue

// break 语句
for i := 0; i < 10; i++ {
    if i == 5 {
        break  // 跳出循环
    }
    fmt.Println(i)
}

// continue 语句
for i := 0; i < 10; i++ {
    if i == 5 {
        continue  // 跳过当前迭代
    }
    fmt.Println(i)
}

// 带标签的 break
// Go语言中的标签(label)可以与break和continue一起使用,用于控制跳出多层循环
// 标签必须放在循环语句的前面,并且以冒号结尾
// 使用break配合标签可以直接跳出指定的外层循环,而不仅仅是最内层循环
// 这在嵌套循环中特别有用,可以避免使用额外的标志变量来控制循环退出
// 标签可以放在任何循环语句前面,不限于最外层循环
// 下面是几种标签使用的示例

// 1. 标签在最外层循环前
outer:
    for i := 0; i < 10; i++ {
        for j := 0; j < 10; j++ {
            if i == 5 && j == 5 {
                break outer  // 跳出外层循环
            }
        }
    }

// 2. 标签在内层循环前
for i := 0; i < 10; i++ {
    inner:
        for j := 0; j < 10; j++ {
            if j == 5 {
                break inner  // 跳出内层循环,效果等同于直接break
            }
        }
}

// 3. 标签也可用于continue语句
loop:
    for i := 0; i < 5; i++ {
        for j := 0; j < 5; j++ {
            if j == 2 {
                continue loop  // 跳过当前外层循环的迭代,直接进入下一次外层循环
            }
        }
    }

实践练习

1. 基础练习

// 练习1:打印九九乘法表
func main() {
    for i := 1; i <= 9; i++ {
        for j := 1; j <= i; j++ {
            fmt.Printf("%d×%d=%d\t", j, i, i*j)
        }
        fmt.Println()
    }
}

// 练习2:判断素数
func isPrime(n int) bool {
    if n <= 1 {
        return false
    }
    for i := 2; i*i <= n; i++ {
        if n%i == 0 {
            return false
        }
    }
    return true
}

2. 进阶练习

// 练习1:斐波那契数列
func fibonacci(n int) []int {
    if n <= 0 {
        return []int{}
    }
    if n == 1 {
        return []int{0}
    }
    fib := make([]int, n)
    fib[0] = 0
    fib[1] = 1
    for i := 2; i < n; i++ {
        fib[i] = fib[i-1] + fib[i-2]
    }
    return fib
}

// 练习2:冒泡排序
func bubbleSort(arr []int) {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        for j := 0; j < n-i-1; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
            }
        }
    }
}

常见问题

1. if 语句问题

  • if 语句中的变量作用域:花括号内 ✓ 正确。if语句中声明的变量只在if语句块内可见,包括else和else if块。
  • 条件判断的短路特性:一旦有一个条件满足,就不会在判断后面的条件 表述不够准确 在逻辑运算中,对于OR(||)操作,一旦左侧条件为true,右侧条件不会被评估;对于AND(&&)操作,一旦左侧条件为false,右侧条件不会被评估。这称为短路求值。
  • 多条件判断的顺序:从左到右判断 ✓ 正确。Go语言中的条件判断总是从左到右进行评估。

2. switch 语句问题

  • case 语句的自动 break ✓ 正确。Go语言中的switch语句默认在每个case执行完后自动break,不需要显式添加break语句。
  • fallthrough 的使用:强制执行下一个case,不需要判断下一个case是否成立 ✓ 正确。fallthrough关键字会强制执行下一个case的语句,而不检查其条件是否满足。
  • switch 语句的类型限制:不知道这个 Go语言中的switch语句有两种主要形式:表达式switch和类型switch。表达式switch比较值,而类型switch使用类型断言比较类型。switch表达式和case值必须是可比较的类型。此外,Go的switch比其他语言更灵活,case可以是表达式,不限于常量或整数。

3. for 循环问题

  • 循环变量的作用域:循环内部 不完全正确 循环变量的作用域取决于声明位置。在for初始化语句中声明的变量(如for i := 0; i < 10; i++中的i)作用域为整个for循环块;而在循环体内声明的变量作用域仅限于循环体内。
  • range 循环的副本问题:不知道这个 在range循环中,迭代变量是每次迭代元素的副本,而不是引用。这意味着修改迭代变量不会影响原始集合。例如,for i, v := range slice {}中,v是slice[i]的副本,修改v不会改变slice[i]的值。
  • 无限循环的退出条件:break 不完全正确 无限循环可以通过多种方式退出:1) break语句;2) return语句(如果在函数内);3) panic;4) goto语句跳转到循环外;5) runtime.Goexit()(终止当前goroutine)。

面试题

  1. Go 语言中的 switch 语句有什么特点?有初始化语句,可以多判断条件,自动break ✓ 正确,还可以补充:Go的switch更灵活,case可以是表达式而不仅是常量,支持类型switch,没有表达式的switch等同于switch true。

  2. for 循环有哪几种形式?for(;;)for实现while,无限for,range for ✓ 正确。Go中for循环有三种基本形式:1) 完整形式for init; condition; post {};2) 类似while的形式for condition {};3) 无限循环for {};4) 使用range的形式for index, value := range collection {}

  3. break 和 continue 的区别是什么? 未回答 break用于立即终止当前循环,并开始执行循环后的代码;continue用于跳过当前循环的剩余部分,直接进入下一次循环。

  4. range 循环中的变量是值拷贝还是引用?range循环中的变量是值拷贝。每次迭代,range提供的是集合元素的副本,而不是引用。这意味着修改这些变量不会影响原始集合中的值。

    // 示例:range循环中的值拷贝
    func main() {
        slice := []int{1, 2, 3, 4, 5}
        
        // 错误的修改方式
        for _, v := range slice {
            v *= 2 // 这里修改的是副本,不会影响原始切片
        }
        fmt.Println(slice) // 输出: [1 2 3 4 5]
        
        // 正确的修改方式
        for i := range slice {
            slice[i] *= 2 // 通过索引修改原始切片
        }
        fmt.Println(slice) // 输出: [2 4 6 8 10]
    }
    
  5. 如何实现带标签的 break? 未回答 Go支持带标签的break,通过在循环前定义标签,然后使用break 标签名可以跳出多层嵌套循环。例如:

参考资源

  1. Go 语言规范
  2. Go 语言圣经
  3. Go 语言官方文档
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值