首页 > 技术文章 > 程序流程控制

xidian2014 2019-03-23 23:38 原文

在程序中,程序运行的流程控制决定程序是如何执行的,主要有三大流程控制语句:顺序控制,分支控制,循环控制。

一、顺序控制

程序从上到下逐行地执行,中间没有任何判断和跳转。

Golang 中定义变量时采用合法的前向引用。

二、分支控制

分支控制就是让程序有选择执行。有三种形式:单分支、双分支、多分支。

1、单分支控制

if 条件表达式{

  执行代码块

}

当条件表达式为true时,就会执行{}的代码。{}是必须要有的,就算只写一行代码。

package main

import "fmt"

func main() {
	//编写一个程序,可以输入人的年龄,如果该同志的年龄大于 18 岁,则输出 "你年龄大 于 18,要对自己的行为负责!"
	var age int
	fmt.Println("请输入年龄")
	fmt.Scanln(&age)

	if age > 18 {
		fmt.Println("你年龄大 于 18,要对自己的行为负责!")
	}
}

 单分支的流程图:

golang支持在if中直接定义一个变量

package main

import "fmt"

func main() {
	//golang支持在if中直接定义一个变量
	if age :=20;age>18{
		fmt.Println("你的年龄大于18,要对自己的行为负责!")
	}
}

 2、双分支控制

if 条件表达式{

  执行代码块1

}else{

  执行代码块2

}

当条件表达式成立时,执行代码块1,否则执行代码块2。{}也是必须有的。

package main

import "fmt"

func main() {
	//编写一个程序,输入年龄,如果年龄大于 18 岁,则输出 “你年龄大于18,要对自己的行为负责!”。否则 输出”你的年龄不大这次放过你了”
	var age int
	fmt.Println("请输入年龄:")
	fmt.Scanln(&age)

	if age > 18 {
		fmt.Println("你年龄大于18,要对自己的行为负责!")
	} else {
		fmt.Println("你的年龄不大这次放过你了")
	}
}

 判断一个年份是否是闰年,闰年的条件是符合下面二者之一:(1)年份能被4整除,但不能被100 整除;(2)能被400整除

package main

import "fmt"

func main() {
	//判断一个年份是否是闰年,闰年的条件是符合下面二者之一:(1)年份能被4整除,但不能被100 整除;(2)能被400整除
	var year int
	fmt.Println("请输入年份")
	fmt.Scanln(&year)

	if (year%4 == 0 && year%100 != 0) || (year%400 == 0) {
		fmt.Println(year, "是闰年")
	}
}

 3、多分支控制

if 条件表达式1{

  执行代码块1

}else if 条件表达式2{

  执行代码块2

}else{

  执行代码块n

}

多分支的判断流程如下:
(1)先判断条件表达式1是否成立,如果为真,就执行代码块1
(2)如果条件表达式1为假,就去判断条件表达式2是否成立, 如果条件表达式2为真, 就执行代码块2
(3)依次类推.
(4)如果所有的条件表达式不成立,则执行else的语句块。
else语句不是必须的。
多分支只能有一个执行入口。

嵌套分支:

在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层分 支外面的分支结构称为外层分支。

基本语法:

if 条件表达式{

  if 条件表达式{

    执行语句1

  }else{

    执行语句2

  }

}else{

  执行语句

}

注意:嵌套分支不宜过多,建议控制在3层内。

4、练习题

(1)、参加百米运动会,如果用时8秒以内进入决赛,否则提示淘汰。并且根据性别提示进入男子组或女子组。

package main

import "fmt"

func main() {
	var second float64

	fmt.Println("请输入秒数")
	fmt.Scanln(&second)

	if second <= 8 {
		var gender string
		fmt.Println("请输入性别")
		fmt.Scanln(&gender)
		if gender == "男" {
			fmt.Println("进入决赛的男子组")
		} else {
			fmt.Println("进入决赛的女子组")
		}
	} else {
		fmt.Println("out...")
	}
}

 (2)、出票系统:根据淡旺季的月份和年龄,打印票价。

package main

import "fmt"

func main() {
	var month byte
	var age byte
	var price float64 = 60.0
	fmt.Println("请输入游玩月份")
	fmt.Scanln(&month)
	fmt.Println("请输入游客年龄")
	fmt.Scanln(&age)

	if month >= 4 && month <= 10 {
		if age > 60 {
			fmt.Printf("%v月 票价 %v 年龄 %v ", month, price/3, age)
		} else if age >= 18 {
			fmt.Printf("%v月 票价 %v 年龄 %v ", month, price, age)
		} else {
			fmt.Printf("%v月 票价 %v 年龄 %v ", month, price/2, age)
		}
	} else {
		if age >= 18 && age < 60 {
			fmt.Println("淡季成人票价40")
		} else {
			fmt.Println("淡季儿童和成人票价20")
		}
	}
}

5、switch分支控制 

switch语句用于基于不同条件执行不同动作,每一个case分支都是唯一的,从上到下逐一测 试,直到匹配为止。
匹配项后面也不需要再加break

switch 表达式{

  case 表达式1,表达式2,... :

    语句块1

  case 表达式3,表达式4,... :

    语句块2

  ......

  default :

    语句块

}

switch执行流程:

(1)、先执行表达式得到值,然后和case的表达式进行比较,如果匹配就执行对应的case语句块,然后退出switch控制。
(2)、如果switch的表达式的值没有和任何的case的表达式匹配成功,则执行default的语句块。执行后退出switch的控制。

golang的case后的表达式可以有多个,使用逗号间隔。
golang中的case语句块不需要写break , 因为默认会有,即在默认情况下,当程序执行完case语句块后,就直接退出该switch控制结构。

package main

import "fmt"

func main() {
	var char byte
	fmt.Println("请输入一个字符")
	fmt.Scanf("%c", &char)

	switch char {
		case 'a':
			fmt.Println("A")
		case 'b':
			fmt.Println("B")
		case 'c':
			fmt.Println("C")
		case 'd':
			fmt.Println("D")
		case 'e':
			fmt.Println("E")
		default:
			fmt.Println("other")
	}
}

switch使用的注意事项:
(1)、case/switch 后是一个表达式( 即:常量值、变量、一个有返回值的函数等都可以)
(2)、case后的各个表达式的值的数据类型,必须和 switch 的表达式数据类型一致
(3)、case后面可以带多个表达式,使用逗号间隔。比如case 表达式 1, 表达式 2 ...
(4)、case后面的表达式如果是常量值(字面量),则要求不能重复
(5)、case后面不需要带break , 程序匹配到一个case后就会执行对应的代码块,然后退出switch,如 果一个都匹配不到,则执行default
(6)、default语句不是必须的
(7)、switch后也可以不带表达式,类似 if else分支来使用。

package main

import "fmt"

func main() {
	//switch后也可以不带表达式,类似 if else分支来使用。
	var age int = 10

	switch {
		case age == 10:
			fmt.Println("age is 10")
		case age == 20:
			fmt.Println("age is 20")
		default:
			fmt.Println("没有匹配")
	}

	//case中也可以对范围进行判断
	var score int = 90
	switch {
		case score > 90:
			fmt.Println("成绩优秀")
		case score >= 70 && score <= 90:
			fmt.Println("成绩良好")
		case score >= 60 && score < 70:
			fmt.Println("成绩及格")
		default:
			fmt.Println("不及格")
	}
}

(8)、switch后也可以直接声明/定义一个变量,分号结束【不推荐】。

package main

import "fmt"

func main() {
	//switch后也可以直接声明/定义一个变量,分号结束【不推荐】
	switch grade := 90; {
		case grade > 90:
			fmt.Println("成绩优秀")
		case grade >= 70 && grade <= 90:
			fmt.Println("成绩良好")
		case grade >= 60 && grade < 70:
			fmt.Println("成绩及格")
		default:
			fmt.Println("不及格")
	}
}

(9)、switch 穿透-fallthrough ,如果在case语句块后增加fallthrough ,则会继续执行下一个case,也叫switch穿透。默认只能穿透一层

package main

import "fmt"

func main() {
	//switch 穿透 fallthrough
	var num int = 10
	switch num {
		case 10:
			fmt.Println("ok")
			fallthrough //默认只能穿透一层
		case 20:
			fmt.Println("ok2")
			fallthrough
		case 30:
			fmt.Println("ok3")
		default:
			fmt.Println("没有匹配到")
	}
}

对学生成绩大于60分的,输出“合格”。低于60分的,输出“不合格”。(注:输入的成绩不能大于100)

package main

import "fmt"

func main() {
	//对学生成绩大于60分的,输出“合格”。低于60分的,输出“不合格”。(注:输入的成绩不能大于100)
	var score float64
	fmt.Println("请输入成绩")
	fmt.Scanln(&score)

	switch int(score / 60) {
		case 1:
			fmt.Println("及格")
		case 0:
			fmt.Println("不及格")
		default:
			fmt.Println("输入有误")
	}
}

 根据用户指定月份,打印该月份所属的季节。3,4,5 春季 6,7,8 夏季 9,10,11 秋季 12, 1, 2 冬季

package main

import "fmt"

func main() {
	//根据用户指定月份,打印该月份所属的季节。3,4,5 春季 6,7,8 夏季 9,10,11 秋季 12, 1, 2 冬季
	var month byte
	fmt.Println("请输入月份")
	fmt.Scanln(&month)

	switch month {
		case 3, 4, 5:
			fmt.Println("spring")
		case 6, 7, 8:
			fmt.Println("summer")
		case 9, 10, 11:
			fmt.Println("autumn")
		case 12, 1, 2:
			fmt.Println("winter")
		default:
			fmt.Println("输入有误")
	}
}

switch和if的比较:

(1)、如果判断的具体数值不多,而且符合整数、浮点数、字符、字符串这几种类型。建议使用swtich语句,简洁高效。
(2)、其他情况:对区间判断和结果为 bool 类型的判断,使用if,if的使用范围更广。

三、循环控制

1、for循环控制

for 循环变量初始化;循环条件;循环变量迭代{

  循环操作语句

}

for循环的四个要素:

(1)、循环变量初始化

(2)、循环条件

(3)、循环操作语句,也叫循环体

(4)、循环变量迭代

循环条件是返回一个布尔值的表达式。

for循环流程图:

package main

import "fmt"

func main() {
	for i := 1; i <= 10; i++ {
		fmt.Println("hello world")
	}
}

for循环的第二种使用方式:

for 循环判断条件{

  循环体

}

package main

import "fmt"

func main() {
	j := 1
	for j <= 10 {
		fmt.Println("golang go", j)
		j++
	}
}

 for循环的第三种使用方式:

for {

  循环体

}

等价于 for ; ;{}是一个无限循环,通常需要配合break语句使用

package main

import "fmt"

func main() {
	k := 1
	for {
		if k <= 10 {
			fmt.Println("ok~~", k)
		} else {
			break
		}
		k++
	}
}

 for - range遍历字符串和数组

package main

import "fmt"

func main() {
	//字符串遍历方式1
	var str string = "hello,world"
	for i := 0; i < len(str); i++ {
		fmt.Printf("%c \t", str[i])
	}
	fmt.Println()

	//字符串遍历方式2 for - range
	str = "abcdef"
	for index, val := range str {
		fmt.Printf("index = %d val = %c\n", index, val)
	}
}

 如果字符串含有中文,那么传统的遍历字符串方式,就是错误,会出现乱码。原因是传统的对字符串的遍历是按照字节来遍历,而一个汉字在utf8编码是对应3个字节。需要要将 str 转成 []rune 切片才能解决。

package main

import "fmt"

func main() {
	//[]rune切片方式遍历有中文的字符串
	var str string = "hello,world北京"
	str2 := []rune(str)
	for i := 0; i < len(str2); i++ {
		fmt.Printf("%c \t", str2[i])
	}
	fmt.Println()

	//for - range 方式遍历有中文的字符串
	str = "abdedf上海"
	for index, val := range str {
		fmt.Printf("index = %d val = %c\n", index, val)
	}
}

 打印1~100之间所有是9的倍数的整数的个数及总和

package main

import "fmt"

func main() {
	//打印1~100之间所有是9的倍数的整数的个数及总和
	var max uint64 = 100
	var count uint64 = 0
	var sum uint64 = 0
	var i uint64 = 1
	for ; i <= max; i++ {
		if i%9 == 0 {
			count++
			sum += i
		}
	}
	fmt.Printf("count = %v sum = %v\n", count, sum)
}

 2、while循环和do while循环

golang语言中没有while和do while循环。

如果需要使用while循环和do while循环类似的循环,可以通过for循环来实现其使用效果。

(1)、while循环的实现:

循环变量初始化

for{

  if 循环条件表达式{

    break  //跳出for循环

  }

  循环体

  循环变量迭代

}

package main

import "fmt"

func main() {
	var i int = 1
	for {
		if i > 10 {
			break
		}
		fmt.Println("hello,world", i)
		i++
	}
	fmt.Println("i = ", i)
}

 (2)、do while循环的实现:

循环变量初始化

for{

  循环体

  循环变量迭代

  if 循环条件表达式{

    break  //跳出for循环

  }

}

package main

import "fmt"

func main() {
	var j int = 1
	for {
		fmt.Println("ok ", j)
		j++
		if j > 10 {
			break
		}
	}
	fmt.Println("j = ", j)
}

 3、多重循环控制

将一个循环放在另一个循环体内,就形成了嵌套循环。在外边的for称为外层循环在里面的for循环称为内层循环。【建议一般使用两层,最多不要超过3层】
实质上,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为false时,才会完全跳出内层循环,才可结束外层的当次循环,开始下一次的循环。
外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次

统计3个班成绩情况,每个班有5名同学,求出各个班的平均分和所有班级的平均分[学生的成 绩从键盘输入]

package main

import "fmt"

func main() {
	//统计3个班成绩情况,每个班有5名同学,求出各个班的平均分和所有班级的平均分[学生的成 绩从键盘输入]
	var classNum int = 2
	var stuNum int = 5
	var totalSum float64 = 0.0

	for i := 1; i <= classNum; i++ {
		sum := 0.0
		for j := 1; j <= stuNum; j++ {
			var score float64
			fmt.Printf("请输入第%d班 第%d个学生的成绩\n", i, j)
			fmt.Scanln(&score)
			sum += score
		}
		fmt.Printf("第%d个班级的平均分是%v\n", i, sum/float64(stuNum))
		totalSum += sum
	}
	fmt.Printf("各个班级的总成绩%v,所有班级平均分是%v\n", totalSum, totalSum/float64(stuNum*classNum))
}

 4、练习题

(1)、打印一个矩形

package main

import "fmt"

func main() {
	for i := 1; i <= 3; i++ {
		for j := 1; j <= 3; j++ {
			fmt.Print("* ")
		}
		fmt.Println()
	}
}

(2)、打印半个金字塔

package main

import "fmt"

func main() {
	for i := 1; i <= 3; i++ {
		for j := 1; j <= i; j++ {
			fmt.Print("* ")
		}
		fmt.Println()
	}
}

(3)、打印整个金字塔

package main

import "fmt"

func main() {
	tower(5)
	fmt.Println()
	tower2(5)
}

func tower(level int) {
	for i := 1; i <= level; i++ {
		for j := 1; j <= level-i; j++ {
			fmt.Print("  ")
		}
		for n := 1; n < i; n++ {
			fmt.Print("* ")
		}
		for k := 1; k <= i; k++ {
			fmt.Print("* ")
		}
		fmt.Println()
	}
}

func tower2(level int) {
	for i := 1; i <= level; i++ {
		for j := 1; j <= level-i; j++ {
			fmt.Print("  ")
		}
		for k := 1; k <= 2*i-1; k++ {
			fmt.Print("* ")
		}
		fmt.Println()
	}
}

(4)、打印空心金字塔

package main

import "fmt"

func main() {
	tower(9)
}

func tower(level int) {
	for i := 1; i <= level; i++ {
		for j := 1; j <= level-i; j++ {
			fmt.Print("  ")
		}
		for k := 1; k <= 2*i-1; k++ {
			if k == 1 || k == 2*i-1 || i == level {
				fmt.Print("* ")
			} else {
				fmt.Print("  ")
			}
		}
		fmt.Println()
	}
}

(5)、打印九九乘法表

package main

import "fmt"

func main() {
	//打印九九乘法表
	var num int = 9
	for i := 1; i <= num; i++ {
		for j := 1; j <= i; j++ {
			fmt.Printf("%v * %v = %v \t", j, i, j*i)
		}
		fmt.Println()
	}
}

 四、跳转控制语句

1、break

随机生成 1-100 的一个数,直到生成了 99 这个数,看看你一共用了几次?

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	var count int = 0
	//time.Now().Unix()返回一个从1970:01:01的0时0分0秒到现在的秒数

	for {
		rand.Seed(time.Now().UnixNano()) //在go中生成随机数需要一个种子,否则返回的值总是固定的。
		n := rand.Intn(100) + 1          //[0 100)
		fmt.Println("n = ", n)
		count++
		if (n == 9) {
			break
		}
	}
	fmt.Println("生成99一共使用了", count, "次")
}

break语句用于终止某个语句块的执行,用于中断当前for循环或跳出switch语句。

break的注意事项和使用细节:
break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块。

package main

import "fmt"

func main() {
	lable2:
		for i := 0; i < 4; i++ {
		//lable1:
			for j := 0; j < 10; j++ {
				if j == 2 {
					break lable2
				}
				fmt.Println("j = ", j)
			}
		}
}

 练习题:100以内的数求和,求出当和第一次大于20的当前数

package main

import "fmt"

func main() {
	//100以内的数求和,求出当和第一次大于20的当前数
	sum := 0
	for i := 1; i <= 100; i++ {
		sum += i
		if sum > 20 {
			fmt.Println("当sum>20时,当前数是", i)
			break
		}
	}
}

2、continue

continue语句用于结束本次循环,继续执行下一次循环。
continue语句出现在多层嵌套的循环语句体中时,可以通过标签指明要跳过的是哪一层循环 , 这 个和前面的break标签的使用的规则一样。

package main

import "fmt"

func main() {
	for i := 0; i < 4; i++ {
		for j := 0; j < 10; j++ {
			if j == 2 {
				continue
			}
			fmt.Println("j = ", j)
		}
		fmt.Println()
	}
}

 打印1——100之内的奇数[要求使用for循环+continue]

package main

import "fmt"

func main() {
	//打印1——100之内的奇数[要求使用for循环+continue]
	for i := 1; i <= 100; i++ {
		if i%2 == 0 {
			continue
		}
		fmt.Println("奇数是", i)
	}
}

 从键盘读入个数不确定的整数,并判断读入的正数和负数的个数,输入为0时结束程序

package main

import "fmt"

func main() {
	//从键盘读入个数不确定的整数,并判断读入的正数和负数的个数,输入为0时结束程序
	var positiveCount int
	var negativeCount int
	var num int

	for {
		fmt.Println("请输入一个整数")
		fmt.Scanln(&num)

		if num == 0 {
			break
		}

		if num > 0 {
			positiveCount++
			continue
		}
		negativeCount++
	}
	fmt.Printf("正数有%v个,负数有%v个\n", positiveCount, negativeCount)
}

 3、goto

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

package main

import "fmt"

func main() {
	var n int = 30

	fmt.Println("ok")

	if n > 20 {
		goto lable1
	}

	fmt.Println("ok1")
	fmt.Println("ok2")
	fmt.Println("ok3")
	lable1:
	fmt.Println("ok4")
	fmt.Println("ok5")
}

 4、return

return使用在方法或者函数中,表示跳出所在的方法或函数。
如果return是在普通的函数,则表示跳出该函数,即不再执行函数中return后面代码,也可以理解成终止函数。
如果return是在main函数,表示终止main函数,也就是说终止程序。

package main

import "fmt"

func main() {
	for i := 1; i <= 10; i++ {
		if i == 3 {
			return
		}
		fmt.Println("i = ", i)
	}
	fmt.Println("hello world")
}

 

推荐阅读