1. 前言

go语言的控制结构和C语言是类似的,本节主要讲解条件语句:if、switch、select 和 循环语句for、for-range。

2. if

package main

import "fmt"

func main() {
    a := 6
    b := 8

    // 省略括号
    if a < b {
        fmt.Println("a 小于 b")
    }

    if a < 3 {
        fmt.Println("a 小于 3")
    } else {
        fmt.Println("a 大于 3")
    }

    // 在if语句中进行赋值
    if c, ok := compare(a); ok {
        fmt.Printf("a 大于 c; a: %d,c: %d",a,c)
    }
}

func compare(a int) (int, bool) {
    c := 2
    var re bool
    if a > c {
        re = true
    } else {
        re = false
    }

    return c, re
}

输出:

a 小于 b
a 大于 3
a 大于 c; a: 6,c: 2

3. switch

go语言里switch默认每个case最后带break,每一个 case 分支都是唯一的,从上往下直到匹配为止

package main

import "fmt"

func main() {
    a := 6

    switch a {
    case 1:  // 空分支只有a=1时才进来
    case 3:
        fmt.Println("只有a=3才进来")
    case 5,8:
        fmt.Println("只有等于5,8才进来")
    default:
        fmt.Println("其它值进入default")
    }

    // 另外表达形式
    b := 8
    switch {
    case b == 1:  // 空分支只有a=1时才进来
    case b == 3:
        fmt.Println("只有b=3才进来")
    case b == 5 || b == 8:
        fmt.Println("只有b等于5,8才进来")
    default:
        fmt.Println("其它值进入default")
    }
}

输出:

其它值进入这里
只有b等于5,8才进来

4. select

select语句类似于switch语句,不同点是switch是从上往下匹配,select是随机执行一个可运行的case(select中的case条件(非阻塞)是并发执行的)。 如果没有 case 可运行,它将阻塞,直到有 case 可运行。

select语句只能用于信道的读写操作,是用来监听和channel有关的IO操作,当 IO 操作发生时,触发相应的动作。

select语法

每个 case 都必须是一个通信
所有 channel 表达式都会被求值
所有被发送的表达式都会被求值
如果任意某个通信可以进行,它就执行,其他被忽略。
如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。
否则:
如果有 default 子句,则执行该语句。
如果没有 default 子句,select 将阻塞,直到某个通信可以运行;Go 不会重新对 channel 或值进行求值。

对于case条件语句中,如果存在信道值为nil的读写操作,则该分支将被忽略,可以理解为从select语句中删除了这个case语句
如果有超时条件语句,判断逻辑为如果在这个时间段内一直没有满足条件的case,则执行这个超时case。 ,则直接执行这个case。一般用超时语句代替了default语句
对于空的select{},会引起死锁
对于for中的select{}, 也有可能会引起cpu占用过高的问题

  • select中的case语句是随机执行的
package main

import "fmt"

func main() {
    size := 10
    ch1 := make(chan int, size)
    for i := 0; i < size; i++ {
        ch1 <- 1
    }

    ch2 := make(chan int, size)
    for i := 0; i < size; i++ {
        ch2 <- 2
    }

    ch3 := make(chan int, 1)

    select {
    case v := <-ch1:
        fmt.Print(v)
    case b := <-ch2:
        fmt.Print(b)
    case ch3 <- 10:
        fmt.Print(3)
    default:
        fmt.Println("default")
    }
}

执行多次,会随机输出不同的值,1、2、3。这是因为ch1和ch2是并发执行会同时返回数据,所以会随机选择一个case执行。 因为上面的三个case都是可以操作的信道,所以永远不会执行default语句。

  • 超时使用
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    go func(c chan int) {
        // 修改时间后,再查看执行结果
        time.Sleep(time.Second * 1)
        ch <- 1
    }(ch)

    select {
    case v := <-ch:
        fmt.Print(v)
    case <-time.After(2 * time.Second): // 等待 2s
        fmt.Println("no case ok")
    }

    time.Sleep(time.Second * 1)
}

如果等待时间超出<2秒,则输出1,否则打印“no case ok”

5. for

package main

import "fmt"

func main() {
    for a := 0; a < 5; a++ {
        fmt.Println(a)
    }

    // 循环嵌套输出 2 到 100 间的素数
    var i, j int
    for i = 2; i < 10; i++ {
        for j = 2; j <= (i / j); j++ {
            if i%j == 0 {
                break // 如果发现因子,则不是素数
            }
        }
        if j > (i / j) {
            fmt.Printf(" %d是素数\n", i)
        }
    }

    // for 条件语句 {};类似于while
    b :=3

    for b>1 {
        b--
        fmt.Println("没有初始化和修饰语句的for结构:", b)
    }

    // 无限循环,只有在循环体里 break才会跳出
    c := 0
    for {
        fmt.Println("无限循环:", c)
        c++
        if c > 3 {
            break
        }
    }
}

输出:

0
1
2
3
4
 2是素数
 3是素数
 5是素数
 7是素数
没有初始化和修饰语句的for结构: 2
没有初始化和修饰语句的for结构: 1
无限循环: 0
无限循环: 1
无限循环: 2
无限循环: 3

6. for-range

这种循环语法很类似其它语言中 foreach 语句,它常用于数组、切片、map、字符串等

package main

import "fmt"

func main() {
    str := "abc"
    fmt.Printf("The length of str is: %d\n", len(str))
    for index, value := range str {
        fmt.Printf(" %d : %c \n", index, value)
    }

    // 忽略value
    for i := range str {
        println(i)
    }
    // 忽略 index。
    for _, c := range str {
        println(string(c))
    }
    // 忽略全部返回值,仅迭代。
    for range str {
        println("只迭代")
    }
}

输出:

The length of str is: 3
 0 : a
 1 : b
 2 : c
0
1
2
a
b
c
只迭代
只迭代
只迭代

7. break与continue

使用 break 语句会退出当前循环,使用 continue 跳过执行直接进入下一次循环. 另外,关键字 continue 只能被用于 for 循环中。

package main

func main() {
    for i := 0; i < 2; i++ {
        for j := 0; j < 8; j++ {
            if j > 3 {
                break
            } else if j == 2 {
                continue
            }
            println(j)
        }
    }
}

8. 标签与goto

标签名区分大小写,定义后若不使用会造成编译错误,标签(label)可在for、switch 或 select 语句中做标识符使用。

goto 语句通常与条件语句配合使用,使用goto可以无条件地转移到过程中指定的行

在结构化程序设计中一般不主张使用 goto、标签语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。

Copyright © yzx该文章修订时间: 2021-10-11 20:08:59

results matching ""

    No results matching ""