LOADING

正在加载

Go基础学习(贰)——三大程序流程结构

壹 程序设计流程结构概述

为什么要学三大流程?原因很简单:

  • a.真的很重要
  • b.在编程时是必须会用到的
  • c.通过三大流程,我可以实现选择操作,例如:是否吃饭、是否睡觉,也可以实现重复执行一件事。

在程序设计语言中,有三种基础的流程结构顺序结构、选择结构和循环结构。许多简单或复杂的算法都可以由这三种基本结构组合而成,是学习任何一门编程语言的基础语法及结构。

贰 顺序语句

按照代码执行的先后顺序,依次执行,程序中的大多数代码都是这样执行的,就可以理解为说明书的下一步一样,像时间的飞逝一样一去不复返,所以真的要好好珍惜时间,大哭!

2.1 顺序语句框图

f9a71d00411e61456cb096e72a00338f.png

2.2 代码

package main
import "fmt"
func main() {
    fmt.Println("程序开始!")
    fmt.Println("语句1!")
    fmt.Println("语句2!")
    fmt.Println("语句结束!")
}

代码演示结果:
216a0309cb405feb7d23ba079f8ddd0b.png

叁 选择语句

条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为 true 来决定是否执行指定语句,并在条件为 false 的情况在执行另外的语句。
bf9fdb503ecac1a2f93fc0dd6f42589c.png

3.1 if 语句

if语句 由一个布尔表达式后紧跟一个或多个语句组成。

3.1.1 语句格式

注意:这里的布尔表达式如果加括号的话,会自动取消掉

// if 在布尔表达式为 true 时,其后紧跟的语句块执行,如果为 false 则不执行。
if 布尔表达式 {
   /* 在布尔表达式为 true 时执行 */
}

3.1.2 语句框图

7b074dd35bb987464fe101c6806843bf.png

3.1.3 代码

package main
import "fmt"
func main() {
    /* 定义局部变量 */
    a := 10
    /* 使用 if 语句判断布尔表达式 */
    if a < 20 {
        /* 如果条件为 true 则执行以下语句 */
        fmt.Printf("a 小于 20\n")
    }
    fmt.Printf("a 的值为 : %d\n", a)
}

代码演示结果:
25f0b6ac95a9bb91808efe71a7d60eeb.png

3.2 if…else 语句

if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。

3.2.1 语句格式

// If 在布尔表达式为 true 时,其后紧跟的语句块执行,如果为 false 则执行 else 语句块。
// 需要说明if语句后的}必须要紧接着else{,即}else{要在同一行
if 布尔表达式 {
   /* 在布尔表达式为 true 时执行 */
} else {
  /* 在布尔表达式为 false 时执行 */
}

3.2.2 语句框图

e3165ff720146ce7fb5e045a62903c54.png

3.2.3 代码

package main
import "fmt"
func main() {
   /* 局部变量定义 */
        a := 100;
   /* 判断布尔表达式 */
   if a < 20 {
       /* 如果条件为 true 则执行以下语句 */
       fmt.Printf("a 小于 20\n" );
   } else {
       /* 如果条件为 false 则执行以下语句 */
       fmt.Printf("a 不小于 20\n" );
   }
   fmt.Printf("a 的值为 : %d\n", a);
}

代码演示结果:
3e7c5db3a52703f295c9a3f604e67d5a.png

3.3 if 嵌套语句

你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。

3.3.1 语句格式

// 你可以以同样的方式在 if 语句中嵌套 else if...else 语句
if 布尔表达式 1 {
   /* 在布尔表达式 1 为 true 时执行 */
} else if 布尔表达式 2 {
  /* 在布尔表达式 2 为 true 时执行 */
} else{
    /* 在布尔表达式都为为 false 时执行 */
}

3.3.2 框图

f26e33fdd6ccde353d817ccb32ccd232.png

3.3.3 代码

package main
import "fmt"
func main() {
    /* 局部变量定义 */
    a := 100
    /* 判断布尔表达式 */
    if a < 100 {
        /* 如果条件为 true 则执行以下语句 */
        fmt.Printf("a 小于 100\n")
    } else if a > 100 {
        /* 如果条件为 false,但是满足另一个条件则执行以下语句 */
        fmt.Printf("a 大于 100\n")
    } else {
        /* 如果条件为 false 则执行以下语句 */
        fmt.Printf("a 等于 100\n")
    }
    fmt.Printf("a 的值为 : %d\n", a)
}

代码演示结果:
b0915cef1cca140546ae522383ec9308.png

3.4 扩展——if条件判断特殊写法

if条件判断还有一种特殊的写法,可以在 if 表达式之前添加一个执行语句,再根据变量值进行判断,举个例子:

package main
import (
    "fmt"
)
func main() {
    // 这里的这个score,只能在if里面使用,即该if作用域内使用,不能出现在if语句外面
    if score := 65; score >= 90 {
        fmt.Println("A")
    } else if score > 75 {
        fmt.Println("B")
    } else {
        fmt.Println("C")
    }
    //输出C
}

3.5 switch 语句

switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。
switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。
switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough

3.5.1 语句格式

变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3。

// 如果var1省略,那么val1,val2...的值必须规定,以满足判断布尔值,意思就是说必须有一个地方进行类似与if语句的情况进行对应的case语句中。
// 一个分支可以有多个值,多个case值中间使用英文逗号分隔。
switch var1 {
    case val1:
        /* 满足val1条件执行的内容 */
    case val2,val3,val4:
        /* 满足val2\cal3\val4条件执行的内容 */
    default:
        /* 以上条件都不满足执行的内容 */
}

3.5.2 语句框图

7d33199392dd2ddd37745b551361ef56.png

3.5.3 代码

a := 1
val := 0
// 第一种方法,最普通的方法
switch a {
case 1:
    // 需要注意,在case里面是不可以声明定义val的,会报没有使用的错误,所以只能外面定义val,其原因可能是(个人理解)因为在case内的变量处于case这个作用域里面,定义的变量只能在该作用域下使用
    val = 1
case 2, 3, 4:
    val = 2
case 5:
    val = 5
case 10:
    val = 11
default:
    val = 0
}
fmt.Println(val)

// 第二种方法,有点像上面说的if语句的特殊写法,前面有一个执行语句
// 与if特殊写法一样,这里的这个b,只能在switch里面使用,即该switch作用域内使用,不能出现在switch语句外面
switch b := 1; b {
case 1:
    // 在这里是不可以定义val的,
    val = 1
case 2, 3, 4:
    val = 2
case 5:
    val = 5
case 10:
    val = 11
default:
    val = 0
}
fmt.Println(val)

// 第三种方法,可以看到这里switch后面不用加b参数
c := 90
switch {
case c < 60:
    // 可以看出case的代码是前面case到后面的case之间的
    fmt.Println("C")
    fmt.Println("C")
case c >= 60 && b < 90:
    fmt.Println("B")
case c >= 90:
    fmt.Println("A")
default:
    fmt.Println("0000")
}
// 可以看出两种方法都需要在case后面进行判断

代码演示结果:
87f2e758ed54d2b7a81db2cbd72cbeef.png

3.6 fallthrough语句

fallthrough语法可以执行满足条件的case的下一个case,是为了兼容C语言中的case设计的,并且fallthrough必须在满足case所要执行的内容的最后。

s := "a"
switch {
case s == "a":
    fmt.Println("a")
    // fallthrough		在这里是错误的
    fmt.Println("a")
    fallthrough
case s == "b":
    fmt.Println("b")
case s == "c":
    fmt.Println("c")
default:
    fmt.Println("...")
}

代码演示结果:
971b673493ad25465cae8a131fd52106.png

3.7 select 语句(了解即可)

一个select语句用来选择哪个case中的发送或接收操作可以被立即执行。它类似于switch语句,但是它的case涉及到channel有关的I/O操作。

3.7.1 语句格式

//select基本用法
select {
    case <- chan1:
        // 如果chan1成功读到数据,则进行该case处理语句
    case chan2 <- 1:
        // 如果成功向chan2写入数据,则进行该case处理语句
    default:
        // 如果上面都没有成功,则进入default处理流程
}
// 可以看到->(写数据)与<-(读数据)的方向问题
  • 每个 case 都必须是一个通信
  • 所有 channel 表达式都会被求值
  • 所有被发送的表达式都会被求值
  • 如果任意某个通信可以进行,它就执行,其他被忽略。
  • 如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。否则:
    • a.如果有 default 子句,则执行该语句。
    • b.如果没有 default 子句,select 将阻塞,直到某个通信可以运行;Go 不会重新对 channel 或值进行求值。

3.7.2 代码

package main

import (
    "fmt"
    "time"
)

func main() {
    start := time.Now()
    c := make(chan interface{})
    ch1 := make(chan int)
    ch2 := make(chan int)
    go func() {
        time.Sleep(4 * time.Second)
        close(c)
    }()
    go func() {
        time.Sleep(3 * time.Second)
        ch1 <- 3
    }()
    go func() {
        time.Sleep(3 * time.Second)
        ch2 <- 5
    }()
    fmt.Println("读数据时阻塞...")
    select {
    case <-c:

        fmt.Printf("稍后解锁 %v.\n", time.Since(start))
    case <-ch1:
        fmt.Printf("ch1 case...")
    case <-ch2:
        fmt.Printf("ch1 case...")
    default:
        fmt.Printf("default go...")
    }
}

代码演示结果:
d10a0b1604403960c08c771f2e5e5555.png

肆 循环语句

在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。
f4914a0884a768b74de2fc8f564cf7d4.png

4.1 for 语句

for 循环是一个循环控制结构,可以执行指定次数的循环。

4.1.1 语句格式

Go 语言的 For 循环基本格式如下:

for 初始语句;条件表达式;结束语句{
    循环体语句
}
//初始语句: 一般为赋值表达式,给控制变量赋初值;
//条件表达式: 关系表达式或逻辑表达式,循环控制条件;
//结束语句: 一般为赋值表达式,给控制变量增量或减量。

For 循环主要有以下 4 种形式:

  • a.第一种正常的for语句
    for init; condition; post {}
    
  • b.第二种是忽略init,但是要写;隔开
    for ;condition; post {}
    
  • c.第三种是忽略init和post,和while语句差不多
    for condition {}
    
  • s.第四种是全部忽略,进入无限循环语句:
    for {}
    

    4.1.2 语句框图

    869aea28270930c9abb13bb3dc35af5a.png

4.1.3 代码

package main
import "fmt"
func main() {
    a := 0
    for i := 0; i <= 10; i++ {
        a++
    }
    fmt.Println("1值为", a)
    for a <= 10 {
        a++
    }
    fmt.Println("2值为", a)
    for {
        a++
    }
    /* 不执行该语句,要停止无限循环,可以在命令窗口按下ctrl-c,这种格式的循环可以对字符串、数组、切片等进行迭代输出元素。 */
    fmt.Println("3值为", a)
}

代码演示结果:
d6f11ae17fcba34d1f54ab6afee9c24a.png

4.2 for…range语句——键值循环

Go语言中可以使用for range遍历数组、切片、字符串、map 及通道(channel)。 通过for range遍历的返回值有以下规律:

  • a.遍历的是数组、切片、字符串,则返回的是索引和值。
  • b.遍历的是map,则返回的是键和值。
  • c.遍历的是通道(channel),则返回的是通道内的值。

    4.2.1 语句格式

    基本格式如下:
    for [键,值] := range 被遍历的参数 {
      循环体语句
    }
    //[键,值]:为获取的键值对,键或者值可以没有,可以为匿名变量,但是必须有一个,并且用英文,隔开
    //被遍历的参数:需要循环遍历的参数
    

    4.2.2 代码

    package main
    import "fmt"
    func main() {
      var str = "hello 你好"
      for key, value := range str {
          fmt.Printf("key:%d value:0x%x\n", key, value)
      }
    }
    
    代码演示结果:
    dad21e6795c4301e49e9fbf26833f4a8.png

注意:在使用for range循环时经常会出现以下情况:

func main() {
    slice := []int{0,1,2,3}
    m := make(map[int]*int)

    for key,val := range slice {
        // value := val
        // m[key] = &value
        m[key] = &val
    }
    for k,v := range m {
        fmt.Println(k,"->",*v)
    }
}
# 我们预期的结果是:
0 -> 0
1 -> 1
2 -> 2
3 -> 3
# 实际结果是:
0 -> 3
1 -> 3
2 -> 3
3 -> 3

出现上面情况的原因是因为for range循环会创建每个元素的副本,而不是元素的引用,所以m[key] = &val取的都是变量val的地址,所以最后map中的所有元素的值都是变量val的地址,因为最后val被赋值为3,所有输出都是3。

4.3 break 语句——跳出循环

Go 语言中 break 语句用于以下两方面:

  • 用于循环语句中跳出循环,并开始执行循环之后的语句。
  • break 在 switch(开关语句)中在执行一条 case 后跳出语句的作用(默认有的)。
  • 在多重循环中,可以用标号 label 标出想 break 的循环。

    4.3.1 语句格式

    break
    

    4.3.2 语句框图

    d6acddcbb065e046c5e0a04b9afd58f9.png

4.3.3 代码

package main
import "fmt"
func main() {
    a, b := 0, 0
    /* 根据前面的例子,我们可以知道for语句可以无限循环,那么我们加break跳出循环*/
    // 不使用标记
    fmt.Println("---- break ----")
    for {
        a++
        if a == 10 {
            break
        }
    }
    fmt.Println("3值为", a)
    a = 0
    
    // 使用标记
    fmt.Println("---- break label ----")
    for i := 0; i <= 10; i++ {
        b++
        fmt.Printf("b值为%d\n", b)
    re:
        for {
            a++
            fmt.Printf("a值为%d\n", a)
            if a > 3 {
                /* 执行后就会跳出re下面的第一个循环 */
                break re
            }
        }
    }
}

代码演示结果:
fd96821c460fcf8b5bef703bcb6abc05.png

4.4 continue 语句——继续下次循环

Go 语言的 continue 语句 有点像 break 语句。但是 continue 不是跳出循环,而是跳过当前循环执行下一次循环语句。for 循环中,执行 continue 语句会触发 for 增量语句的执行。在多重循环中,可以用标号 label 标出想 continue 的循环。

4.4.1 语句格式

continue

4.4.2 语句框图

6ee4c801b2ef626b4610e249f13b3ce2.png

4.4.3 代码

package main
import "fmt"
func main() {
    // 不使用标记
    fmt.Println("---- break ----")
    b := 0
    for i := 0; i <= 10; i++ {
        b++
        if b%3 == 0 {
            continue
        }
        fmt.Printf("b值为%d\n", b)
    }
    // 使用标记
    fmt.Println("---- break label ----")
    b = 0
    for i := 0; i <= 3; i++ {
    re:
        for j := 0; j <= 3; j++ {
            b++
            if b%3 == 0 {
                continue re
            }
            fmt.Printf("b值为%d\n", b)
        }
        fmt.Printf("-b值为%d\n", b)
    }
}

代码演示结果:
f100a2cd9fa0099aa5f493d45acb8e77.png

4.5 goto 语句(建议不用比较危险)

Go 语言的 goto 语句可以无条件地转移到过程中指定的行。goto 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。但是,在结构化程序设计中一般不主张使用 goto 语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。

4.5.1 语句格式

goto label;
..
.
label: statement;

4.5.2 语句框图

285d290b073f4c85dec4fc646cfe1a86.png

4.5.3 代码

package main
import "fmt"
func main() {
    /* 定义局部变量 */
    a := 0
loop:
    for a < 10 {
        if a == 5 {
            a++
            goto loop
        }
        fmt.Printf("a值%d\n", a)
        a++
    }
}

代码演示结果:
99d9c602c81f855d6f82e55f941b78a1.png

avatar
小C&天天

修学储能 先博后渊


今日诗句