• 3.程序控制


    程序控制

    单分支

    Go语言中,花括号一定要跟着if、for、func等行的最后,否则语法出错。

    if condition {
      代码块
    }
    if 8 > 3 {
      fmt.Println("5 greater than 2")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • condition必须是一个bool类型,在Go中,不能使用其他类型等效为布尔值。 if 1 {} 是错误的
    • 语句块中可以写其他代码
    • 如果condition为true,才能执行其后代码块

    多分支

    if condition1 {
      代码块1
    } else if condition2 {
      代码块2
    } else if condition3 {
      代码块3
    } ... {
      ...
    } else if conditionN {
      代码块N
    } else {
      代码块
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    package main
    
    import "fmt"
    
    func main() {
    	a := 6
    	if a < 0 {
    		fmt.Println("negative")
    	} else if a > 0 { // 走到这里一定 a 不小于 0
    		fmt.Println("positive")
    	} else { // 走到这里一定 a 不大于、也不小于 0
    		fmt.Println("zero")
    	}
    	
    positive
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 多分支结构,从上向下依次判断分支条件,只要一个分支条件成立,其后语句块将被执行,那么其他条件都不会被执行
    • 前一个分支条件被测试过,下一个条件相当于隐含着这个条件
    • 一定要考虑一下else分支是有必要写,以防逻辑漏洞
    package main
    
    import "fmt"
    
    func main() {
    	// 嵌套
    	a := 6
    	if a == 0 {
    		fmt.Println("zero")
    	} else {
    		if a > 0 {
    			fmt.Println("negative")
    		} else if a >= 0 { // 走到这里一定 a 不小于 0
    			fmt.Println("positive")
    		}
    	}
    }
    negative
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    循环也可以互相嵌套,形成多层循环。循环嵌套不易过深

    switch分支

    Go语言的switch有别于C语言的switch,case是独立代码块,不能穿透。

    package main
    
    import "fmt"
    
    func main() {
    	// 嵌套
    	switch a := 20; a { // 待比较的是a
    	case 10:
    		fmt.Println("ten")
    	case 20:
    		fmt.Println("twenty")
    	case 30, 40, 50: // 或关系
    		fmt.Println(">=30 and <=50")
    	default:
    		fmt.Println("other")
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    package main
    
    import "fmt"
    
    func main() {
    	// 嵌套
    	a := 20
    	switch { // 没有待比较变量,意味着表达式是true,是布尔型
    	case a > 0:
    		fmt.Println("positive")
    	case a < 0:
    		fmt.Println("negative")
    	default:
    		fmt.Println("zero")
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    package main
    
    import "fmt"
    
    func main() {
    	switch a := 20; { // 没有待比较变量,意味着表达式是true,是布尔型
    	case a > 0: // 如果待比较值是true,a > 0如果返回true,就进入
    		fmt.Println("positive")
    	case a < 0: // 如果待比较值是true,a < 0如果返回true,就进入
    		fmt.Println("negative")
    	default:
    		fmt.Println("zero")
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    package main
    
    import "fmt"
    
    func main() {
    	switch a := 20; { // 没有待比较变量,意味着表达式是true,是布尔型
    	case a > 0: // 如果待比较值是true,a > 0如果返回true,就进入
    		fmt.Println("positive")
    		fallthrough // 穿透
    	case a < 0: // 如果待比较值是true,a < 0如果返回true,就进入
    		fmt.Println("negative")
    	default:
    		fmt.Println("zero")
    	}
    }
    
    positive
    negative
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在Go语言中实现穿透效果,使用fallthrough穿透当前case语句块。但是,大家使用C语言的时候,一般都不想要使用这种穿透效果,所以,如非必要,不要使用fallthrough。

    特殊if

    switch可以写成 switch a:=20;a 这种形式,也就是可以在表达式a之前写一个语句后接一个分号。if也可以这样。

    这种写法中定义的变量作用域只能是当前if或switch。

    if score, line := 99, 90; score > line {
    fmt.Println("perfect")
    } else {
    fmt.Println("good")
    } // score, line作用域只能是当前if语句
    
    • 1
    • 2
    • 3
    • 4
    • 5

    for 循环

    Go语言没有提供while关键字,可以用for方便的替代

    for [初始操作];[循环条件];[循环后操作] {
      循环体
    }
    
    • 1
    • 2
    • 3
    • 初始操作:第一次进入循环前执行,语句只能执行一次,之后不再执行
    • 循环条件:要求返回布尔值,每次进入循环体前进行判断。如果每次条件满足返回true,就进入循环执行一次循环体;否则,循环结束
    • 循环后操作:每次循环体执行完,在执行下一趟循环条件判断之前,执行该操作一次
    for i := 0; i < 10; i++ {
    fmt.Println(i)
    } // 初始操作中的短格式定义的i的作用域只能在for中
    
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    // 特殊写法
    for i := 5; i < 10; {}
    for i := 5; ; {} // 没条件就相当于true
    for i < 10 {} // for condition {},condition就是循环条件
    for ;; {} // 死循环
    // 死循环简写如下
    for {} // 死循环 相对于 for true {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    continue

    中止当前这一趟循环体的执行,直接执行“循环后操作”后,进入下一趟循环的条件判断

    for i := 0; i < 10; i++ {
    if i%2 == 0 {
    continue
    }
    fmt.Println(i)
    }                    把当前符合条件的跳出去在执行 
    
    1
    3
    5
    7
    9
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    break

    终止当前循环的执行,结束了

    package main
    
    import "fmt"
    
    func main() {
    	for i := 0; ; i++ {
    		if i%2 == 0 {
    			continue
    		}
    		fmt.Println(i)
    		if i >= 10 {
    			break
    		}
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    除了break,函数的return结束函数执行,当然也能把函数中的循环打断。

    goto和label

    这是一个被很多语言尘封或者废弃的关键字,它会破坏结构化编程,但是确实能做到便利的无条件跳转。
    跳出多重循环使用,但是问题是为什么要用多重循环?
    到同一处标签处统一处理,例如统一错误处理。问题是,写个函数也可以实现。
    有时候也能简化一些代码,但是它是双刃剑,不要轻易使用。
    goto需要配合标签label使用,label就像代码中的锚点,goto将无条件跳到那里开始向后执行代码。

    package main
    
    import "fmt"
    
    func main() {
    	for i := 0; ; i++ {
    		if i%2 == 0 {
    			continue
    		}
    		fmt.Println(i)
    		if i > 10 {
    			goto condition
    		}
    	}
    condition:
    	fmt.Println("done")
    }
    1
    3
    5
    7
    9
    11
    done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    continue、break也可以指定label,方便某些循环使用。但是,建议不要这么写,弄不好就成了毛线团。

    for range

    for range 结构是Go语言特有的一种的迭代结构,在许多情况下都非常有用,for range 可以遍历数组、切片、字符串、map 及通道(channel),for range 语法上类似于其它语言中的 foreach 语句,一般形式为:

    for key, val := range coll {
        ...
    }
    
    • 1
    • 2
    • 3
    package main
    
    import "fmt"
    
    func main() {
    	for i, v := range "abcd测试" {
    		fmt.Printf("%d, %[2]d, %[2]c, %#[2]x\n", i, v)
    	}
    }
    
    0, 97, a, 0x61
    1, 98, b, 0x62
    2, 99, c, 0x63
    3, 100, d, 0x64
    4, 27979, 测, 0x6d4b
    7, 35797, 试, 0x8bd5      
    
    //"abcd测试"  '测'在字符串中的是属于字符 在字符串中占三个字节
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    func main() {
    	// for i, v := range "abcd测试" {
    	// 	fmt.Printf("%d, %[2]d, %[2]c, %#[2]x\n", i, v)
    	// }
    	fmt.Println("\xe6\xb5\x8b\xe8\xaf\x95") // 6个字节
    }
    测试
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    索引就是字节偏移量,从索引可以看出,中文在字符串中是utf-8编码,占3个字节。
    但是for range读取字符串返回的是一个个字符(整数),而字符是ASCII或UNICODE对应的编码值。
    %d 打印的是unicode值
    %c 打印的是字符

    package main
    
    import "fmt"
    
    func main() {
    	arr := [5]int{1, 3, 5, 7, 9}
    	for i, v := range arr {
    		fmt.Println(i, v, arr[i])
    	}
    	for i := range arr {
    		fmt.Println(i, arr[i])
    	}
    	for _, v := range arr {
    		fmt.Println(v)
    	}
    }
    
    
    0 1 1
    1 3 3
    2 5 5
    3 7 7
    4 9 9
    0 1
    1 3
    2 5
    3 7
    4 9
    1
    3
    5
    7
    9
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    随机数

    标准库"math/rand"

    在1.20.X版本后 直接可以得到随机数,不用指定公式和随机数源

    package main
    
    import (
    	"fmt"
    	"math/rand"
    )
    
    func main() {
    
    	fmt.Println(rand.Intn(20))               //20 代表20以内的数
    }
    
    //多次执行出现结果
    4 
    19 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    package main
    
    import (
    	"fmt"
    	"math/rand"
    )
    
    func main() {
    	for i := 0; i < 10; i++ {
    		fmt.Println(rand.Intn(20))
    	}
    
    }
    //获得10个20以内的随机数
    
    3
    15
    7
    9
    19
    4
    14
    16
    10
    13      //并且执行多次  随机数一直在变化 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在1.20.X版本前

    我们使用的是伪随机数,是内部写好的公式计算出来的。这个公式运行提供一个种子,有这个种子作为起始值开始计算。

    • src := rand.NewSource(100),使用种子100创建一个随机数源
    • rand.New(rand.NewSource(time.Now().UnixNano())) ,利用当前时间的纳秒值做种子
    • r10 := rand.New(src),使用源创建随机数生成器
    • r10.Intn(5),返回[0, 5)的随机整数
    func main() {
    	src := rand.NewSource(10)   使用种子10创建一个随机数源
    	gen10 := rand.New(src)		使用源创建随机数生成器
    
    	for i := 0; i < 10; i++ {
    		fmt.Println(gen10.Intn(20))  返回[0, 20)的随机整数
    	}
    
    }
    14
    8
    7
    19
    8
    4
    19
    15
    15
    18
    我们重复执行 随机数是一模一样的  因为随机数源是固定的 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    因此我们只需要将随机数源的值变成随机的就可以改变

    package main
    
    import (
    	"fmt"
    	"math/rand"
    	"time"
    )
    
    func main() {
    	src := rand.NewSource(time.Now().UnixNano())   //
    	gen10 := rand.New(src)
    
    	for i := 0; i < 5; i++ {
    		fmt.Println(gen10.Intn(20))
    	}
    
    }
    9
    12
    17
    14
    9
    多次执行 发现随机数也是改变的
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
  • 相关阅读:
    传输层协议 --TCP报文格式详细介绍
    《第三期(先导课)》之《Python 开发环境搭建》
    2022年更新宠物 鼠 鱼 十二生肖 宠物养殖系统区块源码
    嵌入式开发:嵌入式基础知识——正确启动固件项目的 10 条建议
    2023.11.17 hadoop之HDFS进阶
    JAVACPU占用过高、内存泄漏问题排查
    闭包
    数据结构 ----- 快速排序
    基于SSM的校园失物招领平台,源码,数据库脚本,项目导入运行视频教程,论文撰写教程
    动态规划:组成目标货币的最少货币数
  • 原文地址:https://blog.csdn.net/xiaolong1155/article/details/134249187