ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

05 流程控制

流程控制

三大流程控制语句:

(1) 顺序控制:程序从上到下逐行的执行,中间没有任何判断和跳转。

(2) 分支控制

(3) 循环控制

说明:控制语句可以使用相互嵌套。

1 分支控制

1.1 if-else分支控制语句

1.1.1 单分支控制

格式:

if condition {
    statements...
}

说明:

(1) 大括号必须存在,哪怕逻辑块中只有1条语句;

(2) 大括号的左边部分{必须位于条件语句的后面,位于同一行;

(3) Golang中的条件表达式condition无需使用括号包围。当为了区分优先级时,可以使用括号;

(4) Golang中的if语句允许条件判断语句里面声明一个变量,这个变量的作用域是if语句的逻辑块中,在其他地方不起作用;变量声明语句与条件判断语句使用分号分隔。示例:if age := 20 ; age >18 {}

1.1.2 双分支控制

格式:

if condition {
    statements...
} else {
    statements...
}

说明:

(1) else关键字必须紧跟在}后面,不能单独一行;建议后面的{接在else关键字后面,位于同一行;

1.1.2 多分支控制

格式:

if condition {
    statements...
} else if condition {
    statements...
} else {
    statements...
}

说明:

(1) else if 可以有多个;else是可选的;

(2) 无论是单分支、双分支还是多分支控制语句,都是只有一个执行入口。

1.2 switch-case分支控制语句

​ switch语句用于条件可以枚举的情况下, 基于不同的条件执行不同的动作。每一个case分支都是唯一的(不能重复),从上往下逐一测试,知道匹配为止。

格式:

switch expression {
case condition1,condition2...: // 可多个condition,中间逗号分隔
    statements...
case condition:
    statements...
......                          // 可以有任意多个case语句
default:                        // default语句是可选的
    statements...
}

说明:

(1) case语句使用冒号结尾;

(2) case后的condition是一个表达式或任何类型的变量(表达式是一个有值的对象。即常量值、变量、一个有返回值的函数等等)。switch后面的的expression也是一个表达式。

(3) case后condition可以有多个,之间使用逗号分隔;各个condition的数据类型必须与switch后面的expression的数据类型一致。常量是无类型的,可以匹配任何类型。

(4) case后condition如果是一个常量值(字面值),则要求多个case间不能有重复的值。如果是变量值,是可以重复的。

(5) Golang中的case语句块不需要写break(这点与bash类似),会默认给添加。即默认情况下,当程序执行完某个case后,就直接退出该switch控制结构(一旦条件符合,自动终止)。

(6) default语句是可选的。

(7) switch后面可以不带表达式(即省略expression),此时case后面使用一个条件表达式。作用类似if-else语句。

func main() {
	var age byte = 20
	switch {
	case age == 10:
		fmt.Println("age=10")
	case age == 20:
		fmt.Println("age=20")
	}
}

(8) switch后面也可以直接声明一个变量,分号结束。使用分号分隔expression和声明语句。不推荐,思路不清晰。

func main() {
	/*
	   switch age := 20; {
	   case age == 10:
	   fmt.Println("age=10")
	   case age == 20:
	   fmt.Println("age=20")
	   }
	*/
	switch age := 20; age {
	case 10:
		fmt.Println("age=10")
	case 20:
		fmt.Println("age=20")
	}
}

(9) swith穿透:在case代码块后使用关键字fallthrough,则会继续执行下一个case,无需判断,可以在多个case中同时使用,默认只穿透一层。

func main() {
	switch age := 10; age {
	case 10:
		fmt.Println("age=10")
		fallthrough
	case 20:
		fmt.Println("age=20")
	}
}
/*
输出:
age=10
age=20
 */

2 循环控制

2.1 for循环控制

完整格式:

for 循环变量初始化; 循环条件; 循环变量迭代 {
    循环操作语句(循环体)
}
//此处的循环变量初始化定义的变量作用域为for代码块
示例
for i := 1; i <= 10; i++ {
	fmt.Println("Hello Wrold!")
}

golang中的循环只有for,没有其他语言中的while,do-while,until循环。

for循环语句四要素:循环变量初始化、循环条件、循环变量迭代、循环操作语句(循环体)。前三者都可以省略。通过三者不同组合的省略,可以实现其他语言中的while,do-while,until的效果。

(1)for循环中,变量初始化是可以省略的(但位置需要保留,即变量初始化语句后面的分号需要保留),将变量初始化写在其他位置:

for ; 循环条件; 循环变量迭代 {
    循环操作语句(循环体)
}
示例循环变量省略
i := 1
for ; i <= 10; i++ {
	fmt.Println("Hello Wrold!")
}

(2)for循环中,变量初始化变量迭代语句可以同时省略,只有一个判断条件(将变量初始化、变量迭代写在其他位置),相当于其他语言中的while循环

for 循环条件 {
    循环操作语句(循环体)
}
示例省略 变量初始化语句变量迭代语句
i := 1
for i <= 10 {
	fmt.Println("Hello Wrold!")
	i++
}

(3)for循环中,变量初始化循环条件变量迭代语句都省略,成为一个死循环:等价于for ; ; {} ,通常需要配合break语句使用

for {
    循环操作语句(循环体)
}

说明:

(1) for循环执行顺序:

1) 循环变量初始化 
2) 循环条件
3) 循环操作语句(循环体
4) 循环变量迭代
5) 重复执行2)、3)、4直到循环条件为false退出循环

(2) 如果再for语句内定义循环变量初始化,定义的变量其作用域为整个for代码块。

(3) 循环条件是一个返回bool值的表达式,循环条件省略是,就成了一个死循环。

(4) Golang中没有while/do-while语句,可以使用for来实现:

while的实现:

循环变量初始化
for 循环条件表达式{
    循环操作语句...
    循环变量迭代
}

或:

循环变量初始化
for {
    if 循环条件表达式 {
        break //跳出for循环
    }
    循环操作语句...
    循环变量迭代
}

**do-while的实现: **至少执行1次循环操作语句:

循环变量初始化
for {
    循环操作语句...
    循环变量迭代
    if 循环条件表达式 {
        break //跳出for循环
    }
}

2.2 for-range循环

2.2.1 for-range

Golang提供了for-range的方式,用来遍历字符串、数组、切片、map、通道。

通过for-range遍历的返回值有如下规律:

​ 1、数组、slice、map、string返回索引(或key)和值;也可以只有一个返回值,此时返回的是索引(或key)。

​ 2、channel只返回通道内的值。

【注意】for-range在遍历字符串时,是按照字符来遍历的,而不是按照字节。

示例:for-range遍历slice

arr := []int{1, 2, 3, 4}
for index, value := range arr {
	fmt.Println("index=", index, " value=", value)
}

2.2.2 字符串遍历

示例:传统for循环遍历字符串

str := "hello world!"
for i := 0; i < len(str); i++ {
	fmt.Printf("%c\n", str[i])
}

注意上例中,字符串不能有汉字。如果字符串有汉字,则会出现乱码。因为这种遍历字符串的方式是按照字节来遍历的,Golang中处理和保存字符串都是以utf8编码进行的,每个汉字使用3个字节。所以会出现乱码。

如何解决?我们可以使用如下2种方法:

方法1:将字符串转换为[]rune,然后再进行遍历:

str1 := "hello 世界!"
str := []rune(str1) //[]rune整体上是一种类型,表示rune类型的切片
for i := 0; i < len(str); i++ {
	fmt.Printf("%c\n", str[i])
}

方法2:可以使用更简便的for-range来遍历字符串:

arr := "hello 世界"
for index, value := range arr {
	fmt.Println("index=", index, " value=", string(value)) //遍历字符串时,value是字符,需要使用string函数转换,或者使用printf的%c打印
}

3 break与continue终止循环

3.1 break终止

(1) break语句用于终止某个语句块的终止,用于终止for循环和switch语句。终止后直接跳出整个for循环或switch语句。

(2) break语句可以出现在多层嵌套for语句中,在Golang中,break默认是终止当前层(最近一层)语句块,也可以通过标签指明要终止的是哪一层的语句块。

func main() {
lable1: //定义一个标签,名字任意,冒号结尾
	for i := 0; i < 4; i++ {
		for j := 0; j < 10; j++ {
			if j == 2 {
				// break
				break lable1  // break了lable1标签下面的for语句
			}
			fmt.Println("j=", j)
		}
	}
}

3.2continue终止

(1) continue语句用于结束本次循环,继续执行下次循环。即执行到continue后,不再执行循环体内后面的代码,直接跳转到循环变量迭代。

(2) continue语句出现在多层嵌套的for循环语句中时,可以通过标签指明要跳过的是哪一层的循环。用法同break。

4 goto跳转

基本语法:

goto label1
....
label1:   //定义一个标签,名字任意,冒号结尾
statements
....

说明:

(1) goto可以无条件地转移到程序中指定的行。

(2) goto语句通常与条件语句配合使用。可用来实现条件转移,跳出循环体等功能。

(3) 在Golang程序设计中一般不建议使用goto语句,以免造成程序流程的混乱,使理解和调试程序都产生困难。

示例:

func main() {
	var n int = 10
	fmt.Println("ok1")
	fmt.Println("ok2")
	if n == 10 {
		goto label1
	}
	fmt.Println("ok3")
	fmt.Println("ok4")
	fmt.Println("ok5")
label1:
	fmt.Println("ok6")
	fmt.Println("ok7")
	fmt.Println("ok8")
}
/*
输出:
ok1
ok2
ok6
ok7
ok8
 */