流程控制语句分类:
流程控制语句可以控制代码的执行顺序和条件
顺序结构:
普通的代码,由上而下依次执行分支结构(if, switch)
循环结构(for)
if语句:
条件表达式值必须是bool类型,不可省略括号,且左大括号不能另起一行
格式1:
if (关系表达式) {语句体;
}
执行流程:
- 首先计算关系表达式的值
- 如果关系表达式的值为true就执行语句体
- 如果关系表达式的值为false就不执行语句体
- 继续执行后面的语句内容
if语句格式2
if (关系表达式) {语句体1;
} else {语句体2;
}
执行流程:
- 首先计算关系表达式的值
- 如果关系表达式的值为true就执行语句体1
- 如果关系表达式的值为false就执行语句体2
- 继续执行后面的语句内容
if语句格式3
if (关系表达式1) {语句体1;
} else if (关系表达式2) {语句体2;
}
…
else {语句体n+1;
}
- 首先计算关系表达式1的值
- 如果值为true就执行语句体1;如果值为false就计算关系表达式2的值
- 如果值为true就执行语句体2;如果值为false就计算关系表达式3的值
- …
- 如果没有任何关系表达式为true,就执行语句体n+1。
演示:
定义一个在0~100之间的变量a, 判断分数在什么范围,90 ~ 100优秀,80 ~ 89良好,70 ~ 79中等,60 ~ 69及格,0 ~ 59请努力加油!
func main() {// 定义一个在0~100之间的变量a, 判断分数在什么范围,90 ~ 100优秀,80 ~ 89良好,70 ~ 79中等,60 ~ 69及格,0 ~ 59请努力加油!var a float64fmt.Println("请输入成绩:")fmt.Scan(&a)if a >= 90 && a <= 100 {fmt.Println("优秀")} else if a >= 80 && a <= 89 {fmt.Println("良好")} else if a >= 70 && a <= 79 {fmt.Println("中等")} else if a >= 60 && a <= 69 {fmt.Println("及格")} else if a >= 0 && a <= 59 {fmt.Println("有脸吃饭?")} else {fmt.Println("成绩需在0~100之间")}
}
死代码:指永远不会被执行的代码,数据被定义成常量时就容易产生死代码
func main() {x := 8if x > 5 { //优先判断,条件表达式结果为true。println("a")} else if x > 7 { // 永远不会执行println("b")}
}
在if中可以定义局部变量赋值后再做条件判断,可以调用函数时传参再判断
func main() {if age := 18; age <= 18 {fmt.Println()}age // 只能在if中使用
}
switch语句:
- switch和case后面是一个表达式,也可以是常量、变量、有返回值的函数,switch后面也可以不带表达式,无表达式时为true
- case常量值不能重复,case后的表达式可以有多个,使用逗号间隔
- Go语言中的switch默认给每个case自带break,因此匹配成功后不会向下执行其他的 case 分支,而是跳出整个switch
- 如需要穿透后续的case须使用fallthrough强制执行下一个的case代码,switch只能穿透一个case,fallthrough必须放在case分支的最后一行,否则编译报错。
- 如果所有的case都和表达式的值不匹配,就会执行default语句体部分,default不是必须要写的
格式:
switch (表达式) {case 1:语句体1case 2:语句体2...default:语句体}
执行流程:
- 首先计算出表达式的值
- 其次,和case依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到break就会结束。
- 最后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后程序结束掉。 所以建议将default放在末尾
定义一个在0~100之间的变量a,判断分数在什么范围,90 ~ 100优秀,80 ~ 89良好,70 ~ 79中等,60 ~ 69及格,0 ~ 59请努力加油!
func main() {var score float64fmt.Println("请输入成绩:")_, err := fmt.Scan(&score)if err != nil {return }switch {case score >= 90 && score <= 100:fmt.Println("优秀")case score >= 80 && score <= 89:fmt.Println("良好")case score >= 70 && score <= 79:fmt.Println("中等")case score >= 60 && score <= 69:fmt.Println("及格")case score >= 0 && score <= 59:fmt.Println("有脸吃饭?")default:fmt.Println("成绩必须在0~100之间")}
}
case表达式值的数据类型必须和switch的数据类型一致
func main() {var num1 int = 1var num2 int64 = 1var num3 float64 = 1switch num1 {case num2: // 报错:'num1' (类型 'int64' 和 'int' 不匹配) 的 switch 中的 case 'num2' 无效fmt.Println(num2)case num3: // 报错:'num1' (类型 'int64' 和 'int' 不匹配) 的 switch 中的 case 'num2' 无效fmt.Println(num2)default:fmt.Println("error")}
}
case的后面可以有多个表达式,使用逗号间隔即可,但case的表达式后面如果跟的是常量值,则要求不能重复
func main() {var num1 int = 1var num2 int = 1switch num1 {case num2, 2, 3:fmt.Println("没问题")case 3: // 报错:case 3的值重复,常量值不能重复fmt.Println("有问题")case num1 + num2: // 这里的结果也是2,但是不是常量值,所以不会报错fmt.Println("没问题")}
}
fallthrough必须放在case块结尾
func main() {var num int = 10switch num {case 10:fmt.Println("case 10 匹配成功")fallthrough // 默认只能穿透一层case 20:fmt.Println("只能穿透到这里")case 30:fmt.Println("case 30 匹配成功")default:fmt.Println("default")}
}
输出:
case 10 匹配成功
只能穿透到这里
fallthrough如果放在default的前面,则会穿透default
func main() {var num int = 30switch num {case 10:fmt.Println("case 10 匹配成功")case 20:fmt.Println("case 20 匹配成功")case 30:fmt.Println("case 30 匹配成功")fallthrough // 默认只能穿透一层default:fmt.Println("default")}
}
输出:
case 30 匹配成功
default
如果fallthrough前面手动执行break就会中断fallthrough执行
func main() {var num int = 50switch num {default:fmt.Println("default在fallthrough前,则不会执行default")case 10:fmt.Println("case 10 匹配成功")case 20:fmt.Println("case 20 匹配成功")break // 如果执行fallthrough前手动break就会中断fallthrough执行fallthrough // 默认只能穿透一层case 30:fmt.Println("走到这里说明被穿透了")}
}
switch还被用来替换if语句,被省略的switch条件表达式默认值为true,继而与case比较表达式结果匹配
func main() {switch x := 5; { // 相当于定义了变量x=5,"switch x := 5; true { ... } ,但是一定要加分号,否则报错case x > 5:fmt.Println("x > 5")case x > 0 && x <= 5: // 多条件是 OR 关系,不能写成“case x > 0, x <= 5”。fmt.Println("x > 0 && x <= 5")default:fmt.Println("default")}
}
fallthrough也可以用来判断接口变量的类型,根据类型再做数据处理。但是如果使用fallthrough,那么其中的case就不能再使用fallthrough
func main() {var x interface{} // 定义空接口var y int64 = 10x = y // 把y赋值给x,此时x的数据类型为int64switch i := x.(type) {case int:fmt.Println("x的值是int", i)case int32:fmt.Println("x的值是int32")case int64:fmt.Println("x的值是int64")case float32:fmt.Println("x的值是float32")//fallthrough 不能使用case float64:fmt.Println("x的值是float64")}
}
for循环
循环语句可以在满足循环条件的情况下,
反复执行某一段代码
,这段被重复执行的代码被称为循环体语句,当反复执行这个循环体时,需要在合适的时候把循环判断条件修改为false,从而结束循环,否则循环将一直执行下去,形成死循环。range和fori循环中的局部变量都会被重复使用
格式
for (初始化语句;条件判断语句;条件控制语句) {循环体语句;
}// 三种for定义格式:func main() {// 格式1for i := 0; i < 3; i++ {}//初始化表达式支持函数调用或定义局部变量。x := 3for x < 10 { // 循环外定义初始化x++}for { // 死循环,相当于"while true {}” 或 “for true {}”。循环体语句}for ; ; {} // 死循环,只不过格式不同
}
格式解释:
初始化语句
: 用于表示循环开启时的起始状态,就是循环开始的时候什么样条件判断语句
:用于表示循环反复执行的条件,就是判断循环是否能一直执行下去循环体语句
: 用于表示循环反复执行的内容,就是循环反复执行的事情条件控制语句
:用于表示循环执行中每次变化的内容,就是控制循环是否能执行下去
执行流程
- 执行初始化语句
- 执行条件判断语句,看其结果是true还是false
如果是false,循环结束
如果是true,继续执行- 执行循环体语句
- 执行条件控制语句
- 回到2继续
求1-100之间的偶数和,并把求和结果在控制台输出
func main() {var sum intfor i := 0; i <= 100; i++ {if i%2 == 0 {sum += i}}fmt.Println(sum)
}// 每次直接加2,不需要if判断
func main() {var sum intfor i := 2; i <= 100; i += 2 {sum += i}fmt.Println(sum)
}
可以遍历字符串和数组
func main() {var name string = "itzhuzhu"for i := 0; i < len(name); i++ {fmt.Printf("%c\n", name[i])}
}
如果遍历的字符串内含有中文会报错,因为遍历是按照字节来的,一个中文占用3个字节,需要转成切片再遍历,或者使用range遍历,range是按照字符遍历的
func main() {var name string = "itzhuzhu我乱码了吗"str := []rune(name)for i := 0; i < len(str); i++ {fmt.Printf("%c\n", str[i])}
}
range方式遍历
func main() {var name string = "haha我乱码了吗"for i, v := range name {fmt.Printf("索引:%d,数据:%c", i, v)}
}
range
- range本质是一个函数,在使用的时候可以加括号使用
- 修改range得到的value不影响原切片/数组的数据,value就是个副本,和原数据无关,但是在range循环里打印的话还是副本的内容
- range和fori循环中的局部变量都会被重复使用
range 关键字用于
- for循环中迭代数组、切片、channel、map的元素
- 在数组和切片中它返回元素的索引值
- 在集合中返回 key-value 对的 key 值
格式:
// for 索引,值 := range 循环数据 for index,value := range arr {}
通过参数列表传递多个参数,使用range获取数据
func main() {//Demo01(1, 2, 3, 4, 5)//Demo02(1, 2, 3, 4, 5)Demo03(1, 2, 3, 4, 5)
}// a代表索引 v代表数据func Demo01(num ...int) {for a, v := range num {fmt.Print("a = ", a, " ")fmt.Println("v = ", v)}
}// 如果只写一个变量,取出的是索引func Demo02(num ...int) {for v := range num {//fmt.Print("a = ", a, " ")fmt.Println("v = ", v)}
}// 可以把a使用_代替,表示是一个匿名变量,匿名变量不会保存具体的数据,就可以用v取出数据func Demo03(num ...int) {for _, v := range num {//fmt.Print("a = ", a, " ")fmt.Println("v = ", v)}
}
演示:
type Student struct {name stringage int
}func main() {s := Student{"韩信", 10}s2 := Student{"娜可露露", 20}s3 := []Student{s, s2}arr := [3]int{1, 2, 3}// range是一个函数,也可以写成 for i, v := range (arr) {// 将arr遍历然后返回给v,所以在range内修改arr的值,不会影响原数据for i, v := range arr {arr[0] = 10fmt.Println("range", i, v)}// 同样的修改v的值也不会影响原数据for _, v := range s3 {v.age = 99fmt.Println("range", v.age)}fmt.Println(arr)fmt.Println(s3)
}
无论是普通for循环,还是range迭代,其定义的局部变量都会重复使用
func main() {str := [3]string{"a", "b", "c"}for i, s := range str {println(&i, &s)}
}
输出:
0x14000062ee0 0x14000062ef8
0x14000062ee0 0x14000062ef8
0x14000062ee0 0x14000062ef8
如果range调用函数,函数只会被执行一次
func data() []int {fmt.Println("初始化")return []int{10, 20, 30}
}func main() {for i, x := range data() {fmt.Println(i, x)}
}
输出:
初始化
0 10
1 20
2 30
跳转控制语句break:
- 跳转控制语句
break
用在循环语句中跳出当前循环,执行循环后面的语句,可以在switch、for中使用 - 多重循环时默认跳出最近的循环,在循环嵌套中可以使用关键字
label
标记要跳出的位置
演示:
func main() {for i := 1; i <= 5; i++ {if i == 3 {break}fmt.Println(i) // 结果是1、2 break会直接跳出for循环}
}
计算100以内的偶数和,当偶数和大于100时停止循环并打印出当前i的值
func main() {sum := 0for i := 0; i < 100; i++ {if i%2 == 0 {sum += iif sum > 100 {fmt.Println("sum>100时,i=", i)break}}}
}
输出:
sum>100时,i= 20
指定跳转:
func main() {lable: // 要跳过的代码前要跳过的代码break lable//要跳过的代码后的位
}
多重循环,内循环为2时结束全部循环
func main() {
label:for i := 0; i < 10; i++ {for j := 0; j < 10; j++ {if j == 2 {break label}fmt.Println(j)}}
}
输出:
0
1
跳转控制语句continue:
跳转控制语句(continue):
满足条件后,跳过本次循环,继续下次循环,基于条件控制, 在循环内部使用,for可以用注意:
continue只能在循环中进行使用!- 在多重循环中可以使用标号
label
跳出循环
判断10以内的奇数并打印
func main() {for i := 1; i <= 5; i++ {if i == 2 {continue}fmt.Println(i) // 结果是1、3、4、5 continue会跳过2,执行后面的}
}
多重循环时判断是否等于2,等于2时跳过当前循环,继续下一次循环
func main() {for i := 0; i < 3; i++ {for j := 0; j < 3; j++ {if j == 2 {continue}fmt.Print(j)}}
}
输出:
010101
跳转控制语句(return):
retur表示终止函数或方法
func main() {// return // 放在这里等于是结束函数for i := 1; i <= 5; i++ {if i == 3 {break}fmt.Println("return前")return // 放在这里是结束循环fmt.Println("return后")fmt.Println(i)}
}
输出:
return前
跳转控制语句(goto)`
跳转控制语句(goto):
转移到指定的行,goto一般配合条件判断使用,可以用来做条件转移,跳出循环等操作
建议尽量不使用goto,但是Go 语言是支持goto
Go语言的goto语句可以无条件地转移到过程中指定的行。
goto语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。但是,在结构化程序设计中一般不主张使用 goto语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。
goto格式:
goto label
...
label : statement
演示:
func main() {goto label // 跳过123,执行45fmt.Println(1)fmt.Println(2)fmt.Println(3)
label:fmt.Println(4)fmt.Println(5)
}
如果向后面跳,会死循环
func main() {
label:fmt.Println("走我了吗")goto label
}
不能跳到其他函数或内部代码块
func gotoTest() {test: // 未使用的标签 'test'fmt.Println("gotoTest...")
}func main() {for i := 0; i < 5; i++ {loop: // 未使用的标签 'loop'fmt.Println("goto03...")}goto loop // 未解析的标签 'loop'goto test // 未解析的标签 'test'
}
循环嵌套
循环嵌套也可以说是多重循环,就是在一个循环体里面嵌入另一个循环
打印乘法口诀表案例:
func forDemo6() {for i := 1; i <= 9; i++ { // 外循环控制行for j := 1; j <= i; j++ { // 内循环控制列fmt.Printf("%d*%d=%d ", j, i, j*1)}fmt.Print("\n")}
}
输出结果:
1*1=1
1*2=1 2*2=2
1*3=1 2*3=2 3*3=3
1*4=1 2*4=2 3*4=3 4*4=4
1*5=1 2*5=2 3*5=3 4*5=4 5*5=5
1*6=1 2*6=2 3*6=3 4*6=4 5*6=5 6*6=6
1*7=1 2*7=2 3*7=3 4*7=4 5*7=5 6*7=6 7*7=7
1*8=1 2*8=2 3*8=3 4*8=4 5*8=5 6*8=6 7*8=7 8*8=8
1*9=1 2*9=2 3*9=3 4*9=4 5*9=5 6*9=6 7*9=7 8*9=8 9*9=9